diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ff3c9605c6..188c11ff31 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,10 +1,10 @@ -# How to contribute to ImageSHarp +# How to contribute to ImageSharp #### **Did you find a bug?** -- Please **ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/JimBobSquarePants/ImageSharp/issues). +- Please **ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/SixLabors/ImageSharp/issues). -- If you're unable to find an open issue addressing the problem, please [open a new one](https://github.com/JimBobSquarePants/ImageSharp/issues/new). Be sure to include a **title, the applicable version, a clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. Please do not hijack existing issues. +- If you're unable to find an open issue addressing the problem, please [open a new one](https://github.com/SixLabors/ImageSharp/issues/new). Be sure to include a **title, the applicable version, a clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. Please do not hijack existing issues. #### **Did you write a patch that fixes a bug?** diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e2c1276eb8..a172605e64 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,7 +3,7 @@ - [ ] I have written a descriptive issue title - [ ] I have verified that I am running the latest version of ImageSharp - [ ] I have verified if the problem exist in both `DEBUG` and `RELEASE` mode -- [ ] I have searched [open](https://github.com/JimBobSquarePants/ImageSharp/issuess) and [closed](https://github.com/JimBobSquarePants/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported +- [ ] 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 ### Description diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4f87647ffc..4be3511650 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,8 @@ ### Prerequisites - [ ] I have written a descriptive pull-request title -- [ ] I have verified that there are no overlapping [pull-requests](https://github.com/JimBobSquarePants/ImageSharp/pulls) open -- [ ] I have verified that I am following matches the existing coding patterns and practise as demonstrated in the repository. These follow strict Stylecop rules :cop:. +- [ ] I have verified that there are no overlapping [pull-requests](https://github.com/SixLabors/ImageSharp/pulls) open +- [ ] I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules :cop:. - [ ] I have provided test coverage for my change (where applicable) ### Description diff --git a/.gitignore b/.gitignore index 8034c8c89c..4942818972 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,9 @@ bld/ # Visual Studo 2015 cache/options directory .vs/ +# Jetbrains Rider cache/options directory +.idea/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -158,7 +161,6 @@ AppPackages/ # Others *.[Cc]ache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl @@ -202,7 +204,8 @@ FakesAssemblies/ **/node_modules **/node_modules/* -**/TestOutput +**/Images/ActualOutput +**/Images/ReferenceOutput # ASP.NET 5 project.lock.json @@ -218,3 +221,4 @@ artifacts/ **/CodeCoverage/* docs/ /samples/AvatarWithRoundedCorner/output +/ImageSharp.Coverage.xml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..e7972649f4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "tests/Images/External"] + path = tests/Images/External + url = https://github.com/SixLabors/Imagesharp.Tests.Images.git + branch = master diff --git a/.travis.yml b/.travis.yml index af8d4ad9de..70501a484b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ matrix: - os: linux # Ubuntu 14.04 dist: trusty sudo: required - dotnet: 1.0.1 + dotnet: 1.0.4 mono: latest # - os: osx # OSX 10.11 # osx_image: xcode7.3.1 @@ -19,6 +19,7 @@ branches: - coverity_scan script: + - git submodule -q update --init - dotnet restore - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1" @@ -26,7 +27,7 @@ env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - - secure: "aim+fUyx7kDQQcAYV1mX16cvyFEYsxiW3y26xjmeuKzsOf6DIUK328pE8KnO50bMWhfVPjuW7jWc43jI+nbUeIW5018aFcjoOrEK2F8JvJ0UKtEo+ONchypJSXA2TSdL0iIlufMBepsmlBsSLkCwHCJYohYcueZV0u9NVPc3n282KLL8ItRZeSFG/cL/a2yrkFnTFhq9OtkUtP4CcVE7BOtzjfftNcn4Rup73e5JkLT7L9AZS7eCYkIYV0KRlT2pOa/FuOHlfP9NP+NVtd83GXUY2FKBsmN3EmrQgGDTfwfwcJjN5dqIqzkIXmLV8IKQ3aiW2//02pIe5VrdqHQG+EVMRcdpCWyKUkMj0g4rGYkqKCtVJojKtOR93ycOGUDc6+cMMoyn3J2qFydkp278dGWeLuwtGfD25fHXorqK1aL9/bGPcwdinrBmcwnuy1IECtuTkEfAPsb6O4nArnDsTEzeQxwa/MAicmpux//TNKgkQGqzCPeHKbl4vOfyyI6kCsf8edWv8fOSPvJUGvL14+/TZ6lY8S+30fosOmwMCe7xlbtcVlBVtOsKx/XUufrP2Vuptlc8INaq6++XtgpCoMLL0SJfBFQKZRmBGavv1Ztyf0aL6Qp303HKGTyXOEq2k18iJmukB6JcnEGVsaAyteGlruQIbPgHWbxhZSoJZPw=" + - secure: "rjMvEMN9rpvIXqXqCAAKzbHyABzr7E4wPU/dYJ/mHBqlCccFpQrEXVVM1MfRFXYuWZSaIioknhLATZjT5xvIYpTNM6D57z4OTmqeRHhYm80=" before_install: - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- @@ -34,7 +35,7 @@ before_install: addons: coverity_scan: project: - name: "JimBobSquarePants/ImageSharp" + name: "SixLabors/ImageSharp" description: "Build submitted via Travis CI" notification_email: james_south@hotmail.com build_command_prepend: "dotnet restore" diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ab214d415..c9c7453f64 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,14 +1,19 @@ { - "version": "0.2.0", - "configurations": [ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ { "name": ".NET Core Launch (console)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceRoot}\\build\\bin\\Debug\\netcoreapp1.1\\build.dll", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceRoot}/samples/AvatarWithRoundedCorner/bin/Debug/netcoreapp1.1/AvatarWithRoundedCorner.dll", "args": [], - "cwd": "${workspaceRoot}\\build", + "cwd": "${workspaceRoot}/samples/AvatarWithRoundedCorner", + // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window "console": "internalConsole", "stopAtEntry": false, "internalConsoleOptions": "openOnSessionStart" diff --git a/APACHE-2.0-LICENSE.txt b/APACHE-2.0-LICENSE.txt deleted file mode 100644 index a666c6e078..0000000000 --- a/APACHE-2.0-LICENSE.txt +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2012 James South - - 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. \ No newline at end of file diff --git a/ImageSharp.ruleset b/ImageSharp.ruleset index 0bd9cd11ab..2149364b1b 100644 --- a/ImageSharp.ruleset +++ b/ImageSharp.ruleset @@ -2,10 +2,10 @@ - - - + \ No newline at end of file diff --git a/ImageSharp.sln b/ImageSharp.sln index d67e869327..f2155f4245 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.14 +VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject @@ -14,7 +14,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt features.md = features.md ImageSharp.ruleset = ImageSharp.ruleset ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings + .github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md NuGet.config = NuGet.config + .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md README.md = README.md EndProjectSection EndProject @@ -30,30 +32,30 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E574A07-F879-4811-9C41-5CBDC6BAFDB7}" ProjectSection(SolutionItems) = preProject src\Shared\AssemblyInfo.Common.cs = src\Shared\AssemblyInfo.Common.cs - src\Shared\stylecop.json = src\Shared\stylecop.json EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Drawing", "src\ImageSharp.Drawing\ImageSharp.Drawing.csproj", "{2E33181E-6E28-4662-A801-E2E7DC206029}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "build", "build\build.csproj", "{575A5002-DD9F-4335-AA47-1DD87FA13645}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\ImageSharp.Tests\ImageSharp.Tests.csproj", "{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Formats.Tiff.Tests", "tests\ImageSharp.Formats.Tiff.Tests\ImageSharp.Formats.Tiff.Tests.csproj", "{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}" EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 @@ -87,18 +89,6 @@ Global {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x64.Build.0 = Release|Any CPU {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x86.ActiveCfg = Release|Any CPU {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x86.Build.0 = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|Any CPU.Build.0 = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x64.ActiveCfg = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x64.Build.0 = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x86.ActiveCfg = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x86.Build.0 = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|Any CPU.ActiveCfg = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|Any CPU.Build.0 = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x64.ActiveCfg = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x64.Build.0 = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x86.ActiveCfg = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x86.Build.0 = Release|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|x64.ActiveCfg = Debug|Any CPU @@ -123,18 +113,6 @@ Global {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x64.ActiveCfg = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x64.Build.0 = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x86.ActiveCfg = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x86.Build.0 = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.Build.0 = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x64.ActiveCfg = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x64.Build.0 = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.ActiveCfg = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.Build.0 = Release|Any CPU {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Debug|x64.ActiveCfg = Debug|x64 @@ -171,6 +149,18 @@ Global {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.Build.0 = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x86.Build.0 = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|Any CPU.Build.0 = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x64.ActiveCfg = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x64.Build.0 = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x86.ActiveCfg = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -179,12 +169,14 @@ Global {9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} - {575A5002-DD9F-4335-AA47-1DD87FA13645} = {E919DF0B-2607-4462-8FC0-5C98FE50F8C9} {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} + {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} EndGlobalSection EndGlobal diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index b058fad4ed..435aad73bf 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -38,10 +38,15 @@ NEXT_LINE_SHIFTED_2 1 1 + False + False False + NEVER False False + NEVER False + ALWAYS False True ON_SINGLE_LINE @@ -342,6 +347,7 @@ True AC DC + DCT EOF FDCT IDCT @@ -369,8 +375,13 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True True True + True True True \ No newline at end of file diff --git a/ImageSharp.v2.ncrunchsolution b/ImageSharp.v2.ncrunchsolution deleted file mode 100644 index b98737f1c0..0000000000 --- a/ImageSharp.v2.ncrunchsolution +++ /dev/null @@ -1,14 +0,0 @@ - - 1 - false - false - true - UseDynamicAnalysis - UseStaticAnalysis - UseStaticAnalysis - UseStaticAnalysis - UseDynamicAnalysis - - - - \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..2eeb57968e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "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. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "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. + + "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). + + "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. + + "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." + + "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. + + 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. + + 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 2018 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. diff --git a/NuGet.config b/NuGet.config index b2c967cc97..322105d4d4 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,6 +1,7 @@  + diff --git a/README.md b/README.md index e46b113793..d9d2628bb7 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,52 @@ - -# ImageSharp ImageSharp - -**ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. +

+ ImageSharp +
+ ImageSharp +
+
+ GitHub license + Gitter + Twitter + OpenCollective + OpenCollective +

+ +### **ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API. + +Without the use of `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments. Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios. -> **ImageSharp** has made excellent progress and contains many great features but is still considered by us to be still in early stages (alpha). As such, we cannot support its use on production environments until the library reaches release candidate status. -> -> Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp). - -[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/APACHE-2.0-LICENSE.txt) -[![GitHub issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues) -[![GitHub stars](https://img.shields.io/github/stars/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/network) -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Twitter](https://img.shields.io/twitter/url/https/github.com/JimBobSquarePants/ImageSharp.svg?style=social)](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%2fJimBobSquarePants%2fImageSharp&via=james_m_south) -[![OpenCollective](https://opencollective.com/imagesharp/backers/badge.svg)](#backers) -[![OpenCollective](https://opencollective.com/imagesharp/sponsors/badge.svg)](#sponsors) - - - -| |Build Status|Code Coverage| -|-------------|:----------:|:-----------:| -|**Linux/Mac**|[![Build Status](https://travis-ci.org/JimBobSquarePants/ImageSharp.svg)](https://travis-ci.org/JimBobSquarePants/ImageSharp)|[![Code coverage](https://codecov.io/gh/JimBobSquarePants/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/JimBobSquarePants/ImageSharp)| -|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/hu6d1gdpxdw0q360/branch/master?svg=true)](https://ci.appveyor.com/project/JamesSouth/imagesharp/branch/master)|[![Code coverage](https://codecov.io/gh/JimBobSquarePants/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/JimBobSquarePants/ImageSharp)| +### Questions? +Do you have questions? We are happy to help! Please [join our gitter channel](https://gitter.im/ImageSharp/General), or ask them on [stackoverflow](https://stackoverflow.com) using the `ImageSharp` tag. ### Installation -At present the code is pre-release but when ready it will be available on [Nuget](http://www.nuget.org). - -**Pre-release downloads** -We already have a [MyGet package repository](https://www.myget.org/gallery/imagesharp) - for bleeding-edge / development NuGet releases. +| Package Name | Release (NuGet) | Nightly (MyGet) | +|--------------------------------|-----------------|-----------------| +| `SixLabors.ImageSharp` | [![NuGet](https://img.shields.io/nuget/v/SixLabors.ImageSharp.svg)](https://www.nuget.org/packages/SixLabors.ImageSharp/) | [![MyGet](https://img.shields.io/myget/sixlabors/v/SixLabors.ImageSharp.svg)](https://www.myget.org/feed/sixlabors/package/nuget/SixLabors.ImageSharp) | +| `SixLabors.ImageSharp.Drawing` | [![NuGet](https://img.shields.io/nuget/v/SixLabors.ImageSharp.Drawing.svg)](https://www.nuget.org/packages/SixLabors.ImageSharp.Drawing/) | [![MyGet](https://img.shields.io/myget/sixlabors/v/SixLabors.ImageSharp.Drawing.svg)](https://www.myget.org/feed/sixlabors/package/nuget/SixLabors.ImageSharp.Drawing) | ### Packages - -The **ImageSharp** library is made up of multiple packages. - -Packages include: -- **ImageSharp** +The **ImageSharp** library is made up of multiple packages: +- **SixLabors.ImageSharp** - Contains the generic `Image` class, PixelFormats, Primitives, Configuration, and other core functionality. - The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats. - Transform methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image. - Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions. -- **ImageSharp.Drawing** +- **SixLabors.ImageSharp.Drawing** - Brushes and various drawing algorithms, including drawing images. - Various vector drawing methods for drawing paths, polygons etc. - Text drawing. -### Manual build +### Build Status -If you prefer, you can compile ImageSharp yourself (please do and help!), you'll need: - -- [Visual Studio 2017 (or above)](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes) -- The [.NET Core SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link. - -Alternatively on Linux you can use: - -- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) -- [.Net Core](https://www.microsoft.com/net/core#linuxubuntu) - -To clone it locally click the "Clone in Windows" button above or run the following git commands. - -```bash -git clone https://github.com/JimBobSquarePants/ImageSharp -``` +| |Build Status|Code Coverage| +|-------------|:----------:|:-----------:| +|**Linux/Mac**|[![Build Status](https://travis-ci.org/SixLabors/ImageSharp.svg)](https://travis-ci.org/SixLabors/ImageSharp)|[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)| +|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/m9pn907xdah3ca39/branch/master?svg=true)](https://ci.appveyor.com/project/six-labors/imagesharp/branch/master)|[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)| ### Features @@ -73,24 +54,17 @@ There's plenty there and more coming. Check out the [current features](features. ### API -Without the constraints of `System.Drawing` We have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. - -Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments. - -Many `Image` methods are also fluent. - Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix. -`Rgba32` is our default PixelFormat, equivalent to `System.Drawing Color`. - On platforms supporting netstandard 1.3+ ```csharp // Image.Load(string path) is a shortcut for our default type. Other pixel formats use Image.Load(string path)) using (Image image = Image.Load("foo.jpg")) { - image.Resize(image.Width / 2, image.Height / 2) - .Grayscale() - .Save("bar.jpg"); // automatic encoder selected based on extension. + image.Mutate(x => x + .Resize(image.Width / 2, image.Height / 2) + .Grayscale()); + image.Save("bar.jpg"); // automatic encoder selected based on extension. } ``` on netstandard 1.1 - 1.2 @@ -100,9 +74,10 @@ using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) using (Image image = Image.Load(stream)) { - image.Resize(image.Width / 2, image.Height / 2) - .Grayscale() - .Save(output); + image.Mutate(x => x + .Resize(image.Width / 2, image.Height / 2) + .Grayscale()); + image.Save(output); } ``` @@ -116,22 +91,33 @@ using (Image image = new Image(400, 400)) } ``` -For optimized access within a loop it is recommended that the following methods are used. +`Rgba32` is our default PixelFormat, equivalent to `System.Drawing Color`. For advanced pixel format usage there are multiple [PixelFormat implementations](https://github.com/SixLabors/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame. -1. `image.GetRowSpan(y)` -2. `image.GetRowSpan(x, y)` +All in all this should allow image processing to be much more accessible to developers which has always been my goal from the start. -For advanced pixel format usage there are multiple [PixelFormat implementations](https://github.com/JimBobSquarePants/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame. +**Check out [this blog post](https://sixlabors.com/blog/announcing-imagesharp-beta-1/) or our [Samples Repository](https://github.com/SixLabors/Samples/tree/master/ImageSharp) for more examples!** -All in all this should allow image processing to be much more accessible to developers which has always been my goal from the start. +### Manual build -### How can you help? +If you prefer, you can compile ImageSharp yourself (please do and help!), you'll need: -Please... Spread the word, contribute algorithms, submit performance improvements, unit tests. +- [Visual Studio 2017 (or above)](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes) +- The [.NET Core SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link. + +Alternatively on Linux you can use: + +- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) +- [.Net Core](https://www.microsoft.com/net/core#linuxubuntu) -Performance is a biggie, if you know anything about the `System.Numerics.Vectors` types and can apply some fancy new stuff with that it would be awesome. +To clone it locally click the "Clone in Windows" button above or run the following git commands. + +```bash +git clone https://github.com/SixLabors/ImageSharp +``` + +### How can you help? -There's a lot of developers out there who could write this stuff a lot better and faster than I and I would love to see what we collectively can come up with so please, if you can help in any way it would be most welcome and benificial for all. +Please... Spread the word, contribute algorithms, submit performance improvements, unit tests, no input is too little. ### The ImageSharp Team @@ -140,9 +126,7 @@ Grand High Eternal Dictator Core Team - [Dirk Lemstra](https://github.com/dlemstra) -- [Jeavon Leopold](https://github.com/jeavon) - [Anton Firsov](https://github.com/antonfirsov) -- [Olivia Ifrim](https://github.com/olivif) - [Scott Williams](https://github.com/tocsoft) ### Backers diff --git a/appveyor.yml b/appveyor.yml index 6b7ba946ec..8321209905 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,25 +4,27 @@ image: Visual Studio 2017 # prevent the double build when a branch has an active PR skip_branch_with_pr: true -init: - - ps: iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/8cf3160c9516ef1f4effc825c0a44acc918a0b5a/appveyor-build-info.ps')) - +before_build: + - git submodule -q update --init + - cmd: dotnet --version + build_script: - cmd: build.cmd test_script: - tests\CodeCoverage\CodeCoverage.cmd -artifacts: -- path: artifacts\bin\ImageSharp\**\*.nupkg +after_test: + - cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.%APPVEYOR_BUILD_VERSION%.nupkg" + - cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.Drawing.%APPVEYOR_BUILD_VERSION%.nupkg" deploy: # MyGet Deployment for builds & releases - - provider: NuGet - server: https://www.myget.org/F/imagesharp/api/v2/package - symbol_server: https://www.myget.org/F/imagesharp/symbols/api/v2/package - api_key: - secure: fz0rUrt3B1HczUC1ZehwVsrFSWX9WZGDQoueDztLte9/+yQG+BBU7UrO+coE8lUf - artifact: /.*\.nupkg/ - on: - branch: master + - provider: NuGet + server: https://www.myget.org/F/sixlabors/api/v2/package + symbol_server: https://www.myget.org/F/sixlabors/symbols/api/v2/package + api_key: + secure: V/lEHP0UeMWIpWd0fiNlY2IgbCnJKQlGdRksECdJbOBdaE20Fl0RNL7WyqHe02o4 + artifact: /.*\.nupkg/ + on: + branch: master diff --git a/build.cmd b/build.cmd index e33a230bcd..6372b41253 100644 --- a/build.cmd +++ b/build.cmd @@ -1,2 +1,17 @@ @echo Off -call build\build.cmd \ No newline at end of file + +PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '.\build.ps1'" + +if not "%errorlevel%"=="0" goto failure + +:success +ECHO successfully built project +REM exit 0 +goto end + +:failure +ECHO failed to build. +REM exit -1 +goto end + +:end \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000000..2f4d1c949f --- /dev/null +++ b/build.ps1 @@ -0,0 +1,113 @@ + +# lets calulat the correct version here +$fallbackVersion = "1.0.0"; +$version = '' + +$tagRegex = '^v?(\d+\.\d+\.\d+)(-([a-zA-Z]+)\.?(\d*))?$' + +# we are running on the build server +$isVersionTag = $env:APPVEYOR_REPO_TAG_NAME -match $tagRegex + + if($isVersionTag){ + + Write-Debug "Building commit tagged with a compatable version number" + + $version = $matches[1] + $postTag = $matches[3] + $count = $matches[4] + Write-Debug "version number: ${version} post tag: ${postTag} count: ${count}" + if("$postTag" -ne ""){ + $version = "${version}-${postTag}" + } + if("$count" -ne ""){ + # for consistancy with previous releases we pad the counter to only 4 places + $padded = $count.Trim().Trim('0').PadLeft(4,"0"); + Write-Debug "count '$count', padded '${padded}'" + + $version = "${version}${padded}" + } + }else { + + Write-Debug "Untagged" + $lastTag = (git tag --list --sort=-taggerdate) | Out-String + $list = $lastTag.Split("`n") + foreach ($tag in $list) { + + Write-Debug "testing ${tag}" + $tag = $tag.Trim(); + if($tag -match $tagRegex){ + Write-Debug "matched ${tag}" + $version = $matches[1]; + break; + } + } + + if("$version" -eq ""){ + $version = $fallbackVersion + Write-Debug "Failed to discover base version Fallback to '${version}'" + }else{ + + Write-Debug "Discovered base version from tags '${version}'" + } + + $buildNumber = $env:APPVEYOR_BUILD_NUMBER + + # build number replacement is padded to 6 places + $buildNumber = "$buildNumber".Trim().Trim('0').PadLeft(6,"0"); + if("$env:APPVEYOR_PULL_REQUEST_NUMBER" -ne ""){ + Write-Debug "building a PR" + + $prNumber = "$env:APPVEYOR_PULL_REQUEST_NUMBER".Trim().Trim('0').PadLeft(5,"0"); + # this is a PR + $version = "${version}-PullRequest${prNumber}${buildNumber}"; + }else{ + Write-Debug "building a branch commit" + + # this is a general branch commit + $branch = $env:APPVEYOR_REPO_BRANCH + + if("$branch" -eq ""){ + $branch = ((git rev-parse --abbrev-ref HEAD) | Out-String).Trim() + + if("$branch" -eq ""){ + $branch = "unknown" + } + } + + $branch = $branch.Replace("/","-").ToLower() + + if($branch.ToLower() -eq "master"){ + $branch = "dev" + } + + $version = "${version}-${branch}${buildNumber}"; + } + } + +if("$env:APPVEYOR_API_URL" -ne ""){ + # update appveyor build number for this build + Invoke-RestMethod -Method "PUT" ` + -Uri "${env:APPVEYOR_API_URL}api/build" ` + -Body "{version:'${version}'}" ` + -ContentType "application/json" +} + +Write-Host "Building version '${version}'" +dotnet restore /p:packageversion=$version + +Write-Host "Building projects" +dotnet build -c Release /p:packageversion=$version + +if ($LASTEXITCODE ){ Exit $LASTEXITCODE } + +if ( $env:CI -ne "True") { + dotnet test ./tests/ImageSharp.Tests/ImageSharp.Tests.csproj --no-build -c Release +} +if ($LASTEXITCODE ){ Exit $LASTEXITCODE } + +Write-Host "Packaging projects" +dotnet pack ./src/ImageSharp/ -c Release --output ../../artifacts --no-build /p:packageversion=$version +if ($LASTEXITCODE ){ Exit $LASTEXITCODE } + +dotnet pack ./src/ImageSharp.Drawing/ -c Release --output ../../artifacts --no-build /p:packageversion=$version +if ($LASTEXITCODE ){ Exit $LASTEXITCODE } diff --git a/build/Program.cs b/build/Program.cs deleted file mode 100644 index 4e59b22145..0000000000 --- a/build/Program.cs +++ /dev/null @@ -1,463 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ConsoleApplication -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text; - using System.Xml; - using LibGit2Sharp; - using Microsoft.Build.Construction; - using Microsoft.Build.Evaluation; - using NuGet.Versioning; - - /// - /// This updates the version numbers for all the projects in the src folder. - /// The version number it will geneate is dependent on if this is a build from master or a branch/PR - /// - /// If its a build on master - /// We take the version number specified in project.json, - /// count how meny commits the repo has had that will affect this project or its dependencies since the version number of manually changed - /// If this is the first commit that effected this project since number change then leave the version number as defined i.e. will build 1.0.0 if thats in project.json - /// unless it is a preview build number in which case we always add the counter - /// - /// If the build is from a PR/branch - /// We take the version number specified in project.json, append a tag for the branch/PR (so we can determin how each package was built) - /// append number of commits effecting the project. - /// - /// - /// - /// for PR#123 and project.json version 2.0.1 and we have had 30 commits affecting the project - /// we would end up with version number 2.0.1-PR124-00030 - /// - /// for branch `fix-stuff` project.json version 2.0.1-alpha1 and we have had 832 commits affecting the project - /// we would end up with version number 2.0.1-alpha1-fix-stuff-00832 - /// - /// for `master` project.json version 2.0.1-alpha1 and we have had 832 commits affecting the project - /// we would end up with version number 2.0.1-alpha1-00832 - /// - /// for `master` project.json version 2.0.1 and we have had 132 commits affecting the project - /// we would end up with version number 2.0.1-CI-00132 - /// - /// for `master` project.json version 2.0.1 and we have had 1 commits affecting the project - /// we would end up with version number 2.0.1 - /// - /// for `master` project.json version 2.0.1-alpha1 and we have had 1 commits affecting the project - /// we would end up with version number 2.0.1-alpha1 - /// - /// - /// TODO Add the option for using this to update the version numbers in a project and its dependent references. - /// - public class Program - { - private const string FallbackTag = "CI"; - - /// - /// Main entry point. - /// - /// The arguments. - public static void Main(string[] args) - { - bool resetmode = args.Contains("reset"); - - // Find the project root - string root = Path.GetFullPath(Path.Combine(LibGit2Sharp.Repository.Discover("."), "..")); - - // Lets find the repo - Repository repo = new LibGit2Sharp.Repository(root); - - // Lets find all the project.json files in the src folder (don't care about versioning `tests`) - IEnumerable projectFiles = Directory.EnumerateFiles(Path.Combine(root, "src"), "*.csproj", SearchOption.AllDirectories); - - ResetProject(projectFiles); - - // Open them and convert them to source projects - List projects = projectFiles.Select(x => ProjectRootElement.Open(x, ProjectCollection.GlobalProjectCollection, true)) - .Select(x => new SourceProject(x, repo.Info.WorkingDirectory)) - .ToList(); - - if (!resetmode) - { - CaclulateProjectVersionNumber(projects, repo); - - UpdateVersionNumbers(projects); - - CreateBuildScript(projects, root); - - foreach (SourceProject p in projects) - { - Console.WriteLine($"{p.Name} {p.FinalVersionNumber}"); - } - } - } - - private static void CreateBuildScript(IEnumerable projects, string root) - { - string outputDir = Path.GetFullPath(Path.Combine(root, @"artifacts\bin\ImageSharp")); - - StringBuilder sb = new StringBuilder(); - foreach (SourceProject p in projects) - { - sb.AppendLine($@"dotnet pack --configuration Release --output ""{outputDir}"" ""{p.ProjectFilePath}"""); - } - - File.WriteAllText("build-inner.cmd", sb.ToString()); - } - - private static void UpdateVersionNumbers(IEnumerable projects) - { - foreach (SourceProject p in projects) - { - // create a backup file so we can rollback later without breaking formatting - File.Copy(p.FullProjectFilePath, $"{p.FullProjectFilePath}.bak", true); - } - - foreach (SourceProject p in projects) - { - // TODO force update of all dependent projects to point to the newest build. - // we skip the build number and standard CI prefix on first commits - string newVersion = p.FinalVersionNumber; - - p.UpdateVersion(newVersion); - } - } - - private static string CurrentBranch(Repository repo) - { - // lets build version friendly commit - string branch = repo.Head.FriendlyName; - - // lets see if we are running in appveyor and if we are use the environment variables instead of the head - string appveryorBranch = Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH"); - if (!string.IsNullOrWhiteSpace(appveryorBranch)) - { - branch = appveryorBranch; - } - - string prNumber = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER"); - if (!string.IsNullOrWhiteSpace(prNumber)) - { - branch = $"PR{int.Parse(prNumber):000}"; - } - - // this will happen when checking out a comit directly and not a branch (like appveryor does when it builds) - if (branch == "(no branch)") - { - throw new Exception("unable to find branch"); - } - - // clean branch names (might need to be improved) - branch = branch.Replace("/", "-").Replace("--", "-"); - - return branch; - } - - private static void CaclulateProjectVersionNumber(List projects, Repository repo) - { - string branch = CurrentBranch(repo); - - // populate the dependency chains - projects.ForEach(x => x.PopulateDependencies(projects)); - - // update the final version based on the repo history and the currentr branch name - projects.ForEach(x => x.CalculateVersion(repo, branch)); - } - - private static void ResetProject(IEnumerable projectPaths) - { - if (File.Exists("build-inner.cmd")) - { - File.Delete("build-inner.cmd"); - } - - // revert the project.json change be reverting it but skipp all the git stuff as its not needed - foreach (string p in projectPaths) - { - if (File.Exists($"{p}.bak")) - { - File.Copy($"{p}.bak", p, true); - File.Delete($"{p}.bak"); - } - } - } - - /// - /// Project level logic - /// - public class SourceProject - { - private readonly IEnumerable dependencies; - private readonly ProjectRootElement project; - - /// - /// Initializes a new instance of the class. - /// - /// The project. - /// The root. - public SourceProject(ProjectRootElement project, string root) - { - this.Name = project.Properties.FirstOrDefault(x => x.Name == "AssemblyTitle").Value; - - this.ProjectDirectory = project.DirectoryPath.Substring(root.Length); - this.ProjectFilePath = project.ProjectFileLocation.File.Substring(root.Length); - this.FullProjectFilePath = Path.GetFullPath(project.ProjectFileLocation.File); - this.Version = new NuGetVersion(project.Properties.FirstOrDefault(x => x.Name == "VersionPrefix").Value); - this.dependencies = project.Items.Where(x => x.ItemType == "ProjectReference").Select(x => Path.GetFullPath(Path.Combine(project.DirectoryPath, x.Include))); - this.FinalVersionNumber = this.Version.ToFullString(); - this.project = project; - } - - /// - /// Gets the project directory. - /// - /// - /// The project directory. - /// - public string ProjectDirectory { get; } - - /// - /// Gets the version. - /// - /// - /// The version. - /// - public NuGetVersion Version { get; private set; } - - /// - /// Gets the dependent projects. - /// - /// - /// The dependent projects. - /// - public List DependentProjects { get; private set; } - - /// - /// Gets the name. - /// - /// - /// The name. - /// - public string Name { get; private set; } - - /// - /// Gets the project file path. - /// - /// - /// The project file path. - /// - public string ProjectFilePath { get; private set; } - - /// - /// Gets the commit count since version change. - /// - /// - /// The commit count since version change. - /// - public int CommitCountSinceVersionChange { get; private set; } = 0; - - /// - /// Gets the full project file path. - /// - /// - /// The full project file path. - /// - public string FullProjectFilePath { get; private set; } - - /// - /// Gets the final version number. - /// - /// - /// The final version number. - /// - public string FinalVersionNumber { get; private set; } - - /// - /// Populates the dependencies. - /// - /// The projects. - public void PopulateDependencies(IEnumerable projects) - { - this.DependentProjects = projects.Where(x => this.dependencies.Contains(x.FullProjectFilePath)).ToList(); - } - - /// - /// Update the version number in the project file - /// - /// the new version number to save. - internal void UpdateVersion(string versionnumber) - { - this.project.AddProperty("VersionPrefix", versionnumber); - this.Version = new NuGetVersion(versionnumber); - this.project.Save(); - } - - /// - /// Calculates the version. - /// - /// The repo. - /// The branch. - internal void CalculateVersion(Repository repo, string branch) - { - foreach (Commit c in repo.Commits) - { - if (!this.ApplyCommit(c, repo)) - { - // we have finished lets populate the final version number - this.FinalVersionNumber = this.CalculateVersionNumber(branch); - - return; - } - } - } - - private bool MatchPath(string path) - { - if (path.StartsWith(this.ProjectDirectory, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (this.DependentProjects.Any()) - { - return this.DependentProjects.Any(x => x.MatchPath(path)); - } - - return false; - } - - private bool ApplyCommitInternal(Commit commit, TreeChanges changes, Repository repo) - { - this.CommitCountSinceVersionChange++; - - // return false if this is a version number root - TreeEntryChanges projectFileChange = changes.Where(x => x.Path?.Equals(this.ProjectFilePath, StringComparison.OrdinalIgnoreCase) == true).FirstOrDefault(); - if (projectFileChange != null) - { - if (projectFileChange.Status == ChangeKind.Added) - { - // the version must have been set here - return false; - } - else - { - Blob blob = repo.Lookup(projectFileChange.Oid); - using (Stream s = blob.GetContentStream()) - { - using (XmlReader reader = XmlReader.Create(s)) - { - ProjectRootElement proj = ProjectRootElement.Create(reader); - NuGetVersion version = new NuGetVersion(proj.Properties.FirstOrDefault(x => x.Name == "VersionPrefix").Value); - if (version != this.Version) - { - // version changed - return false; - } - } - } - } - - // version must have been the same lets carry on - return true; - } - - return true; - } - - private bool ApplyCommit(Commit commit, Repository repo) - { - foreach (Commit parent in commit.Parents) - { - TreeChanges changes = repo.Diff.Compare(parent.Tree, commit.Tree); - - foreach (TreeEntryChanges change in changes) - { - if (!string.IsNullOrWhiteSpace(change.OldPath)) - { - if (this.MatchPath(change.OldPath)) - { - return this.ApplyCommitInternal(commit, changes, repo); - } - } - - if (!string.IsNullOrWhiteSpace(change.Path)) - { - if (this.MatchPath(change.Path)) - { - return this.ApplyCommitInternal(commit, changes, repo); - } - } - } - } - - return true; - } - - private string CalculateVersionNumber(string branch) - { - string version = this.Version.ToFullString(); - - // master only - if (this.CommitCountSinceVersionChange == 1 && branch == "master") - { - if (this.Version.IsPrerelease) - { - // prerelease always needs the build counter just not on a branch name - return $"{version}-{this.CommitCountSinceVersionChange:00000}"; - } - - // this is the full release happy path, first commit after changing the version number - return version; - } - - string rootSpecialVersion = string.Empty; - - if (this.Version.IsPrerelease) - { - // probably a much easy way for doing this but it work sell enough for a build script - string[] parts = version.Split(new[] { '-' }, 2); - version = parts[0]; - rootSpecialVersion = parts[1]; - } - - // if master and the version doesn't manually specify a prerelease tag force one on for CI builds - if (branch == "master") - { - if (!this.Version.IsPrerelease) - { - branch = FallbackTag; - } - else - { - branch = string.Empty; - } - } - - if (rootSpecialVersion.Length > 0) - { - rootSpecialVersion = "-" + rootSpecialVersion; - } - - if (branch.Length > 0) - { - branch = "-" + branch; - } - - int maxLength = 20; // dotnet will fail to populate the package if the tag is > 20 - maxLength -= rootSpecialVersion.Length; // this is a required tag - maxLength -= 7; // for the counter and dashes - - if (branch.Length > maxLength) - { - branch = branch.Substring(0, maxLength); - } - - return $"{version}{rootSpecialVersion}{branch}-{this.CommitCountSinceVersionChange:00000}"; - } - } - } -} diff --git a/build/Properties/launchSettings.json b/build/Properties/launchSettings.json deleted file mode 100644 index d175ae7547..0000000000 --- a/build/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "build": { - "commandName": "Project", - "commandLineArgs": "reset" - } - } -} \ No newline at end of file diff --git a/build/build.cmd b/build/build.cmd deleted file mode 100644 index 14fc0bdaa9..0000000000 --- a/build/build.cmd +++ /dev/null @@ -1,18 +0,0 @@ -@echo Off -set buildRoot="%cd%" - -ECHO Restoring packages -dotnet restore - -ECHO Updating version numbers and generating build script -cd %~dp0 -dotnet run -- update -cd %buildRoot% - -ECHO Building package -call %~dp0build-inner.cmd - -ECHO Reset version numbers -cd %~dp0 -dotnet run -- reset -cd %buildRoot% \ No newline at end of file diff --git a/build/build.csproj b/build/build.csproj deleted file mode 100644 index dc431284ed..0000000000 --- a/build/build.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - netcoreapp1.1 - portable - true - build - Exe - build - ..\ImageSharp.ruleset - - - - - - - \ No newline at end of file diff --git a/build/reset-versions.cmd b/build/reset-versions.cmd deleted file mode 100644 index 31d1d24318..0000000000 --- a/build/reset-versions.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo Off - -set buildRoot="%cd%" -cd %~dp0 - -dotnet run -- reset - -cd %buildRoot% \ No newline at end of file diff --git a/config.wyam b/config.wyam deleted file mode 100644 index 3a4b64c540..0000000000 --- a/config.wyam +++ /dev/null @@ -1,4 +0,0 @@ -#recipe Docs -Settings[Keys.Host] = "imagesharp.org"; -Settings[Keys.Title] = "Image Sharp"; -FileSystem.OutputPath = "./docs"; \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs index b164c8d3bc..087bbc29d5 100644 --- a/samples/AvatarWithRoundedCorner/Program.cs +++ b/samples/AvatarWithRoundedCorner/Program.cs @@ -1,69 +1,109 @@ - +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using SixLabors.Shapes; namespace AvatarWithRoundedCorner { - using System; - using System.Numerics; - using ImageSharp; - using SixLabors.Primitives; - using SixLabors.Shapes; - - class Program + static class Program { static void Main(string[] args) { System.IO.Directory.CreateDirectory("output"); + using (var img = Image.Load("fb.jpg")) + { + // as generate returns a new IImage make sure we dispose of it + using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 20))) + { + destRound.Save("output/fb.png"); + } - GenerateAvatar("fb.jpg", "output/fb.png", new Size(200, 200), 20); - GenerateAvatar("fb.jpg", "output/fb-round.png", new Size(200, 200), 100); - GenerateAvatar("fb.jpg", "output/fb-rounder.png", new Size(200, 200), 150); + using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 100))) + { + destRound.Save("output/fb-round.png"); + } + + using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 150))) + { + destRound.Save("output/fb-rounder.png"); + } + + using (Image destRound = img.CloneAndConvertToAvatarWithoutApply(new Size(200, 200), 150)) + { + destRound.Save("output/fb-rounder-without-apply.png"); + } + + // the original `img` object has not been altered at all. + } } - private static void GenerateAvatar(string source, string destination, Size size, float cornerRadius) + // 1. The short way: + // Implements a full image mutating pipeline operating on IImageProcessingContext + // We need the dimensions of the resized image to deduce 'IPathCollection' needed to build the corners, + // so we implement an "inline" image processor by utilizing 'ImageExtensions.Apply()' + private static IImageProcessingContext ConvertToAvatar(this IImageProcessingContext processingContext, Size size, float cornerRadius) { - using (var image = Image.Load(source)) + return processingContext.Resize(new ResizeOptions { - image.Resize(new ImageSharp.Processing.ResizeOptions - { - Size = size, - Mode = ImageSharp.Processing.ResizeMode.Crop - }); + Size = size, + Mode = ResizeMode.Crop + }).Apply(i => ApplyRoundedCorners(i, cornerRadius)); + } - ApplyRoundedCourners(image, cornerRadius); - image.Save(destination); - } + // 2. A more verbose way, avoiding 'Apply()': + // First we create a resized clone of the image, then we draw the corners on that instance with Mutate(). + private static Image CloneAndConvertToAvatarWithoutApply(this Image image, Size size, float cornerRadius) + { + Image result = image.Clone( + ctx => ctx.Resize( + new ResizeOptions + { + Size = size, + Mode = ResizeMode.Crop + })); + + ApplyRoundedCorners(result, cornerRadius); + return result; } - public static void ApplyRoundedCourners(Image img, float cornerRadius) + // This method can be seen as an inline implementation of an `IImageProcessor`: + // (The combination of `IImageOperations.Apply()` + this could be replaced with an `IImageProcessor`) + public static void ApplyRoundedCorners(Image img, float cornerRadius) { - var corners = BuildCorners(img.Width, img.Height, cornerRadius); - // now we have our corners time to draw them - img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) + IPathCollection corners = BuildCorners(img.Width, img.Height, cornerRadius); + + // mutating in here as we already have a cloned original + img.Mutate(x => x.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) { - BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background - }); + BlenderMode = PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background + })); } - public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) + public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) { // first create a square - var rect = new SixLabors.Shapes.RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); + var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); // then cut out of the square a circle so we are left with a corner - var cornerToptLeft = rect.Clip(new SixLabors.Shapes.EllipsePolygon(cornerRadius-0.5f, cornerRadius - 0.5f, cornerRadius)); + IPath cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius)); // corner is now a corner shape positions top left - //lets make 3 more positioned correctly, we cando that by translating the orgional artound the center of the image - var center = new Vector2(imageWidth / 2, imageHeight / 2); - var angle = Math.PI / 2f; + //lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image + var center = new Vector2(imageWidth / 2F, imageHeight / 2F); - float rightPos = imageWidth - cornerToptLeft.Bounds.Width +1; + float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1; float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; // move it across the widthof the image - the width of the shape - var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); - var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); - var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); + IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); + IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); + IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); } diff --git a/samples/ChangeDefaultEncoderOptions/Program.cs b/samples/ChangeDefaultEncoderOptions/Program.cs index dab8d445ca..a8fbd75993 100644 --- a/samples/ChangeDefaultEncoderOptions/Program.cs +++ b/samples/ChangeDefaultEncoderOptions/Program.cs @@ -1,6 +1,9 @@ -using System; -using ImageSharp; -using ImageSharp.Formats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg; namespace ChangeDefaultEncoderOptions { @@ -10,7 +13,7 @@ namespace ChangeDefaultEncoderOptions { // lets switch out the default encoder for jpeg to one // that saves at 90 quality and ignores the matadata - Configuration.Default.SetEncoder(ImageFormats.Jpeg, new ImageSharp.Formats.JpegEncoder() + Configuration.Default.SetEncoder(ImageFormats.Jpeg, new JpegEncoder() { Quality = 90, IgnoreMetadata = true diff --git a/src/ImageSharp.Drawing/Brushes/Brushes.cs b/src/ImageSharp.Drawing/Brushes/Brushes.cs index e39f3dd497..47e207e8c4 100644 --- a/src/ImageSharp.Drawing/Brushes/Brushes.cs +++ b/src/ImageSharp.Drawing/Brushes/Brushes.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// A collection of methods for creating generic brushes. /// diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs index 33b516e5c2..bb907281b0 100644 --- a/src/ImageSharp.Drawing/Brushes/IBrush.cs +++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Brush represents a logical configuration of a brush which can be used to source pixel colors /// @@ -33,6 +32,6 @@ namespace ImageSharp.Drawing /// The when being applied to things like shapes would usually be the /// bounding box of the shape not necessarily the bounds of the whole image /// - BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options); + BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 57fcd8ffa6..2d29e23fe5 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of an image brush for painting images within areas. /// @@ -22,19 +21,28 @@ namespace ImageSharp.Drawing.Brushes /// /// The image to paint. /// - private readonly ImageBase image; + private readonly ImageFrame image; /// /// Initializes a new instance of the class. /// /// The image. - public ImageBrush(ImageBase image) + public ImageBrush(ImageFrame image) { this.image = image; } + /// + /// Initializes a new instance of the class. + /// + /// The image. + public ImageBrush(Image image) + : this(image.Frames.RootFrame) + { + } + /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new ImageBrushApplicator(source, this.image, region, options); } @@ -47,7 +55,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The source image. /// - private readonly ImageBase source; + private readonly ImageFrame source; /// /// The y-length. @@ -76,7 +84,7 @@ namespace ImageSharp.Drawing.Brushes /// The image. /// The region. /// The options - public ImageBrushApplicator(ImageBase target, ImageBase image, RectangleF region, GraphicsOptions options) + public ImageBrushApplicator(ImageFrame target, ImageFrame image, RectangleF region, GraphicsOptions options) : base(target, options) { this.source = image; @@ -119,7 +127,7 @@ namespace ImageSharp.Drawing.Brushes { int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; - Span sourceRow = this.source.GetRowSpan(sourceY); + Span sourceRow = this.source.GetPixelRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { @@ -130,7 +138,7 @@ namespace ImageSharp.Drawing.Brushes overlay[i] = pixel; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 5fcb92bdc7..844df0e0e9 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of a pattern brush for painting patterns. /// @@ -93,7 +92,7 @@ namespace ImageSharp.Drawing.Brushes } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new PatternBrushApplicator(source, this.pattern, this.patternVector, options); } @@ -116,7 +115,7 @@ namespace ImageSharp.Drawing.Brushes /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageBase source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) : base(source, options) { this.pattern = pattern; @@ -164,7 +163,7 @@ namespace ImageSharp.Drawing.Brushes overlay[i] = this.pattern[patternY, patternX]; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 29c625d7f8..ca6f7630d9 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Brushes.Processors +{ /// /// primitive that converts a point in to a color for discovering the fill color based on an implementation /// @@ -23,7 +21,7 @@ namespace ImageSharp.Drawing.Processors /// /// The target. /// The options. - internal BrushApplicator(ImageBase target, GraphicsOptions options) + internal BrushApplicator(ImageFrame target, GraphicsOptions options) { this.Target = target; @@ -40,7 +38,7 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the destinaion /// - protected ImageBase Target { get; } + protected ImageFrame Target { get; } /// /// Gets the blend percentage @@ -80,7 +78,7 @@ namespace ImageSharp.Drawing.Processors overlay[i] = this[x + i, y]; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index 27aa99b975..ba2fca4e4b 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of a brush that can recolor an image /// @@ -58,7 +57,7 @@ namespace ImageSharp.Drawing.Brushes public TPixel TargeTPixel { get; } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new RecolorBrushApplicator(source, this.SourceColor, this.TargeTPixel, this.Threshold, options); } @@ -93,7 +92,7 @@ namespace ImageSharp.Drawing.Brushes /// Color of the target. /// The threshold . /// The options - public RecolorBrushApplicator(ImageBase source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) + public RecolorBrushApplicator(ImageFrame source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) : base(source, options) { this.sourceColor = sourceColor.ToVector4(); @@ -159,7 +158,7 @@ namespace ImageSharp.Drawing.Brushes overlay[i] = this[offsetX, y]; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 21ebb88a04..658164339d 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of a solid brush for painting solid color areas. /// @@ -43,7 +42,7 @@ namespace ImageSharp.Drawing.Brushes public TPixel Color => this.color; /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new SolidBrushApplicator(source, this.color, options); } @@ -59,7 +58,7 @@ namespace ImageSharp.Drawing.Brushes /// The source image. /// The color. /// The options - public SolidBrushApplicator(ImageBase source, TPixel color, GraphicsOptions options) + public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) : base(source, options) { this.Colors = new Buffer(source.Width); @@ -93,16 +92,23 @@ namespace ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); - - using (var amountBuffer = new Buffer(scanline.Length)) + try { - for (int i = 0; i < scanline.Length; i++) + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); + + using (var amountBuffer = new Buffer(scanline.Length)) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; - } + for (int i = 0; i < scanline.Length; i++) + { + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + } - this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer); + } + } + catch (Exception) + { + throw; } } } diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 03eb7be289..d55e224162 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing.Processors; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The location to draw the blended image. /// The options. /// The . - public static Image DrawImage(this Image source, Image image, Size size, Point location, GraphicsOptions options) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Size size, Point location, GraphicsOptions options) where TPixel : struct, IPixel { if (size == default(Size)) @@ -37,7 +35,7 @@ namespace ImageSharp location = Point.Empty; } - source.ApplyProcessor(new DrawImageProcessor(image, size, location, options), source.Bounds); + source.ApplyProcessor(new DrawImageProcessor(image, size, location, options)); return source; } @@ -49,7 +47,7 @@ namespace ImageSharp /// The image to blend with the currently processing image. /// The opacity of the image image to blend. Must be between 0 and 1. /// The . - public static Image Blend(this Image source, Image image, float percent) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float percent) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; @@ -66,7 +64,7 @@ namespace ImageSharp /// The blending mode. /// The opacity of the image image to blend. Must be between 0 and 1. /// The . - public static Image Blend(this Image source, Image image, PixelBlenderMode blender, float percent) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; @@ -83,7 +81,7 @@ namespace ImageSharp /// The image to blend with the currently processing image. /// The options, including the blending type and belnding amount. /// The . - public static Image Blend(this Image source, Image image, GraphicsOptions options) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, GraphicsOptions options) where TPixel : struct, IPixel { return DrawImage(source, image, default(Size), default(Point), options); @@ -99,7 +97,7 @@ namespace ImageSharp /// The size to draw the blended image. /// The location to draw the blended image. /// The . - public static Image DrawImage(this Image source, Image image, float percent, Size size, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float percent, Size size, Point location) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; @@ -118,7 +116,7 @@ namespace ImageSharp /// The size to draw the blended image. /// The location to draw the blended image. /// The . - public static Image DrawImage(this Image source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index b3ee2ed996..2e5d311c6d 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Processors; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,10 +21,10 @@ namespace ImageSharp /// The details how to fill the region of interest. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Apply(new FillProcessor(brush, options)); + return source.ApplyProcessor(new FillProcessor(brush, options)); } /// @@ -36,7 +34,7 @@ namespace ImageSharp /// The image this method extends. /// The details how to fill the region of interest. /// The . - public static Image Fill(this Image source, IBrush brush) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) where TPixel : struct, IPixel { return source.Fill(brush, GraphicsOptions.Default); @@ -49,7 +47,7 @@ namespace ImageSharp /// The image this method extends. /// The color. /// The . - public static Image Fill(this Image source, TPixel color) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color)); @@ -64,10 +62,10 @@ namespace ImageSharp /// The region. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Apply(new FillRegionProcessor(brush, region, options)); + return source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); } /// @@ -78,7 +76,7 @@ namespace ImageSharp /// The brush. /// The region. /// The . - public static Image Fill(this Image source, IBrush brush, Region region) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) where TPixel : struct, IPixel { return source.Fill(brush, region, GraphicsOptions.Default); @@ -93,7 +91,7 @@ namespace ImageSharp /// The region. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, Region region, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Region region, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), region, options); @@ -107,7 +105,7 @@ namespace ImageSharp /// The color. /// The region. /// The . - public static Image Fill(this Image source, TPixel color, Region region) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Region region) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), region); diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 10562b08ca..3e320dccc7 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -1,50 +1,54 @@  - - An extension to ImageSharp that allows the drawing of images, paths, and text. - ImageSharp.Drawing - 1.0.0-alpha9 - James Jackson-South and contributors - netstandard1.1 - true - true - ImageSharp.Drawing - ImageSharp.Drawing - Image Draw Shape Path Font - https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-128.png - https://github.com/JimBobSquarePants/ImageSharp - http://www.apache.org/licenses/LICENSE-2.0 - git - https://github.com/JimBobSquarePants/ImageSharp - false - false - false - false - false - false - false - false - false - full - portable - True - - - - - - - - - - - - All - - - - ..\..\ImageSharp.ruleset - - - true - + + An extension to ImageSharp that allows the drawing of images, paths, and text. + SixLabors.ImageSharp.Drawing + $(packageversion) + 0.0.1 + SixLabors and contributors + netstandard1.1;netstandard2.0 + true + true + SixLabors.ImageSharp.Drawing + SixLabors.ImageSharp.Drawing + Image Draw Shape Path Font + https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png + https://github.com/SixLabors/ImageSharp + http://www.apache.org/licenses/LICENSE-2.0 + git + https://github.com/SixLabors/ImageSharp + false + false + false + false + false + false + false + false + false + full + portable + True + + + + + + + + + + + + + + All + + + + ..\..\ImageSharp.ruleset + SixLabors.ImageSharp.Drawing + + + true + \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Paths/DrawBeziers.cs b/src/ImageSharp.Drawing/Paths/DrawBeziers.cs index 59bcf40363..de4fdd003b 100644 --- a/src/ImageSharp.Drawing/Paths/DrawBeziers.cs +++ b/src/ImageSharp.Drawing/Paths/DrawBeziers.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points)), options); @@ -43,7 +41,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, PointF[] points) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); @@ -58,7 +56,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawBeziers(this Image source, TPixel color, float thickness, PointF[] points) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.DrawBeziers(new SolidBrush(color), thickness, points); @@ -74,7 +72,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawBeziers(this Image source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.DrawBeziers(new SolidBrush(color), thickness, points, options); @@ -89,7 +87,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawBeziers(this Image source, IPen pen, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new CubicBezierLineSegment(points)), options); @@ -103,7 +101,7 @@ namespace ImageSharp /// The pen. /// The points. /// The . - public static Image DrawBeziers(this Image source, IPen pen, PointF[] points) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, PointF[] points) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new CubicBezierLineSegment(points))); diff --git a/src/ImageSharp.Drawing/Paths/DrawLines.cs b/src/ImageSharp.Drawing/Paths/DrawLines.cs index 3ce0dc4da6..e5d9a1b3b4 100644 --- a/src/ImageSharp.Drawing/Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing/Paths/DrawLines.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawLines(this Image source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); @@ -43,7 +41,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawLines(this Image source, IBrush brush, float thickness, PointF[] points) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); @@ -58,7 +56,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawLines(this Image source, TPixel color, float thickness, PointF[] points) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.DrawLines(new SolidBrush(color), thickness, points); @@ -74,7 +72,7 @@ namespace ImageSharp /// The points. /// The options. /// The .> - public static Image DrawLines(this Image source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.DrawLines(new SolidBrush(color), thickness, points, options); @@ -89,7 +87,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawLines(this Image source, IPen pen, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IPen pen, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points)), options); @@ -103,7 +101,7 @@ namespace ImageSharp /// The pen. /// The points. /// The . - public static Image DrawLines(this Image source, IPen pen, PointF[] points) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IPen pen, PointF[] points) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points))); diff --git a/src/ImageSharp.Drawing/Paths/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPath.cs index 1fba06370d..b6c821a60b 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPath.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(pen.StrokeFill, new ShapePath(path, pen), options); @@ -39,7 +37,7 @@ namespace ImageSharp /// The pen. /// The path. /// The . - public static Image Draw(this Image source, IPen pen, IPath path) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) where TPixel : struct, IPixel { return source.Draw(pen, path, GraphicsOptions.Default); @@ -55,7 +53,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), path, options); @@ -70,7 +68,7 @@ namespace ImageSharp /// The thickness. /// The path. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), path); @@ -86,7 +84,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPath path, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path, options); @@ -101,7 +99,7 @@ namespace ImageSharp /// The thickness. /// The path. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPath path) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPath path) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path); diff --git a/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs index 877737653d..a126663b05 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The paths. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { foreach (IPath path in paths) @@ -44,7 +42,7 @@ namespace ImageSharp /// The pen. /// The paths. /// The . - public static Image Draw(this Image source, IPen pen, IPathCollection paths) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) where TPixel : struct, IPixel { return source.Draw(pen, paths, GraphicsOptions.Default); @@ -60,7 +58,7 @@ namespace ImageSharp /// The shapes. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), paths, options); @@ -75,7 +73,7 @@ namespace ImageSharp /// The thickness. /// The paths. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPathCollection paths) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPathCollection paths) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), paths); @@ -91,7 +89,7 @@ namespace ImageSharp /// The paths. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, paths, options); @@ -106,7 +104,7 @@ namespace ImageSharp /// The thickness. /// The paths. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPathCollection paths) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPathCollection paths) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, paths); diff --git a/src/ImageSharp.Drawing/Paths/DrawPolygon.cs b/src/ImageSharp.Drawing/Paths/DrawPolygon.cs index 4fa469a496..771ea9e616 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPolygon.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); @@ -43,7 +41,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, PointF[] points) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); @@ -58,7 +56,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawPolygon(this Image source, TPixel color, float thickness, PointF[] points) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.DrawPolygon(new SolidBrush(color), thickness, points); @@ -74,7 +72,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawPolygon(this Image source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.DrawPolygon(new SolidBrush(color), thickness, points, options); @@ -88,7 +86,7 @@ namespace ImageSharp /// The pen. /// The points. /// The . - public static Image DrawPolygon(this Image source, IPen pen, PointF[] points) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IPen pen, PointF[] points) where TPixel : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default); @@ -103,7 +101,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawPolygon(this Image source, IPen pen, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IPen pen, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options); diff --git a/src/ImageSharp.Drawing/Paths/DrawRectangle.cs b/src/ImageSharp.Drawing/Paths/DrawRectangle.cs index b3f0e6fc3b..6b98d1f8e9 100644 --- a/src/ImageSharp.Drawing/Paths/DrawRectangle.cs +++ b/src/ImageSharp.Drawing/Paths/DrawRectangle.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height), options); @@ -39,7 +37,7 @@ namespace ImageSharp /// The pen. /// The shape. /// The . - public static Image Draw(this Image source, IPen pen, RectangleF shape) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) where TPixel : struct, IPixel { return source.Draw(pen, shape, GraphicsOptions.Default); @@ -55,7 +53,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), shape, options); @@ -70,7 +68,7 @@ namespace ImageSharp /// The thickness. /// The shape. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, RectangleF shape) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, RectangleF shape) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), shape); @@ -86,7 +84,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, shape, options); @@ -101,7 +99,7 @@ namespace ImageSharp /// The thickness. /// The shape. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, RectangleF shape) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, RectangleF shape) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, shape); diff --git a/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs b/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs index abb5ef73a7..fff082f2d9 100644 --- a/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs +++ b/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using System; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The shape. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, Action path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Action path, GraphicsOptions options) where TPixel : struct, IPixel { var pb = new PathBuilder(); @@ -42,7 +40,7 @@ namespace ImageSharp /// The brush. /// The path. /// The . - public static Image Fill(this Image source, IBrush brush, Action path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Action path) where TPixel : struct, IPixel { return source.Fill(brush, path, GraphicsOptions.Default); @@ -57,7 +55,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, Action path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Action path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path, options); @@ -71,7 +69,7 @@ namespace ImageSharp /// The color. /// The path. /// The . - public static Image Fill(this Image source, TPixel color, Action path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Action path) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path); diff --git a/src/ImageSharp.Drawing/Paths/FillPathCollection.cs b/src/ImageSharp.Drawing/Paths/FillPathCollection.cs index 3ea9fb94b8..b252b95d5b 100644 --- a/src/ImageSharp.Drawing/Paths/FillPathCollection.cs +++ b/src/ImageSharp.Drawing/Paths/FillPathCollection.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The shapes. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { foreach (IPath s in paths) @@ -43,7 +41,7 @@ namespace ImageSharp /// The brush. /// The paths. /// The . - public static Image Fill(this Image source, IBrush brush, IPathCollection paths) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPathCollection paths) where TPixel : struct, IPixel { return source.Fill(brush, paths, GraphicsOptions.Default); @@ -58,7 +56,7 @@ namespace ImageSharp /// The paths. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), paths, options); @@ -72,7 +70,7 @@ namespace ImageSharp /// The color. /// The paths. /// The . - public static Image Fill(this Image source, TPixel color, IPathCollection paths) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPathCollection paths) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), paths); diff --git a/src/ImageSharp.Drawing/Paths/FillPaths.cs b/src/ImageSharp.Drawing/Paths/FillPaths.cs index f579c4ad02..f554ed7581 100644 --- a/src/ImageSharp.Drawing/Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing/Paths/FillPaths.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The shape. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), options); @@ -38,7 +36,7 @@ namespace ImageSharp /// The brush. /// The path. /// The . - public static Image Fill(this Image source, IBrush brush, IPath path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) where TPixel : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); @@ -53,7 +51,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, IPath path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path, options); @@ -67,7 +65,7 @@ namespace ImageSharp /// The color. /// The path. /// The . - public static Image Fill(this Image source, TPixel color, IPath path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPath path) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path); diff --git a/src/ImageSharp.Drawing/Paths/FillPolygon.cs b/src/ImageSharp.Drawing/Paths/FillPolygon.cs index 6266d3bd64..d8723bc31f 100644 --- a/src/ImageSharp.Drawing/Paths/FillPolygon.cs +++ b/src/ImageSharp.Drawing/Paths/FillPolygon.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -27,7 +25,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image FillPolygon(this Image source, IBrush brush, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, IBrush brush, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); @@ -41,7 +39,7 @@ namespace ImageSharp /// The brush. /// The points. /// The . - public static Image FillPolygon(this Image source, IBrush brush, PointF[] points) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, IBrush brush, PointF[] points) where TPixel : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points))); @@ -56,7 +54,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image FillPolygon(this Image source, TPixel color, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, TPixel color, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); @@ -70,7 +68,7 @@ namespace ImageSharp /// The color. /// The points. /// The . - public static Image FillPolygon(this Image source, TPixel color, PointF[] points) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, TPixel color, PointF[] points) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); diff --git a/src/ImageSharp.Drawing/Paths/FillRectangle.cs b/src/ImageSharp.Drawing/Paths/FillRectangle.cs index bd6460cf90..52578de178 100644 --- a/src/ImageSharp.Drawing/Paths/FillRectangle.cs +++ b/src/ImageSharp.Drawing/Paths/FillRectangle.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Fill(this Image source, IBrush brush, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height), options); @@ -38,7 +36,7 @@ namespace ImageSharp /// The brush. /// The shape. /// The . - public static Image Fill(this Image source, IBrush brush, RectangleF shape) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) where TPixel : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height)); @@ -53,7 +51,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), shape, options); @@ -67,7 +65,7 @@ namespace ImageSharp /// The color. /// The shape. /// The . - public static Image Fill(this Image source, TPixel color, RectangleF shape) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, RectangleF shape) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), shape); diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs index ec4f222e09..61f1291c45 100644 --- a/src/ImageSharp.Drawing/Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using System; - using System.Buffers; - using System.Numerics; - - using SixLabors.Shapes; +using System; +using System.Buffers; +using System.Numerics; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp.Drawing +{ /// /// A mapping between a and a region. /// diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index a7a72c4f3c..a96b03dd04 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using System; - using System.Buffers; - using System.Numerics; - using ImageSharp.Memory; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System; +using System.Buffers; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp.Drawing +{ /// /// A mapping between a and a region. /// @@ -44,18 +42,18 @@ namespace ImageSharp.Drawing public override Rectangle Bounds { get; } /// - public override int Scan(float y, Span buffer) + public override int Scan(float y, float[] buffer, int offset) { var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); using (var innerBuffer = new Buffer(buffer.Length)) { - var span = innerBuffer.Span; - int count = this.Shape.FindIntersections(start, end, span); + PointF[] array = innerBuffer.Array; + int count = this.Shape.FindIntersections(start, end, array, 0); for (int i = 0; i < count; i++) { - buffer[i] = span[i].X; + buffer[i + offset] = array[i].X; } return count; diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs index 20ac53dafc..0680ec2f56 100644 --- a/src/ImageSharp.Drawing/Pens/IPen.cs +++ b/src/ImageSharp.Drawing/Pens/IPen.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Pens -{ - using ImageSharp.PixelFormats; - using Processors; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Pens +{ /// /// Interface representing a Pen /// diff --git a/src/ImageSharp.Drawing/Pens/Pens.cs b/src/ImageSharp.Drawing/Pens/Pens.cs index 364115cb77..6478b1b813 100644 --- a/src/ImageSharp.Drawing/Pens/Pens.cs +++ b/src/ImageSharp.Drawing/Pens/Pens.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Pens -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Pens +{ /// /// Common Pen styles /// diff --git a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs index 37cfff9e93..74e4c6596d 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Pens -{ - using System; - using System.Numerics; - - using ImageSharp.Drawing.Brushes; - using ImageSharp.PixelFormats; - using Processors; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Pens +{ /// /// Provides a pen that can apply a pattern to a line with a set brush and thickness /// diff --git a/src/ImageSharp.Drawing/PointInfo.cs b/src/ImageSharp.Drawing/PointInfo.cs deleted file mode 100644 index 7eff24fac8..0000000000 --- a/src/ImageSharp.Drawing/PointInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - /// - /// Returns details about how far away from the inside of a shape and the color the pixel could be. - /// - public struct PointInfo - { - /// - /// The distance along path - /// - public float DistanceAlongPath; - - /// - /// The distance from path - /// - public float DistanceFromPath; - } -} diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 62344b1012..47763c0aaf 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Processors +{ /// /// Combines two images together by blending the pixels. /// @@ -43,7 +41,7 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the image to blend. /// - public Image Image { get; private set; } + public Image Image { get; } /// /// Gets the alpha percentage value. @@ -61,23 +59,24 @@ namespace ImageSharp.Drawing.Processors public Point Location { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Image disposableImage = null; Image targetImage = this.Image; try { - if (targetImage.Bounds.Size != this.Size) + if (targetImage.Size() != this.Size) { - targetImage = disposableImage = new Image(this.Image).Resize(this.Size.Width, this.Size.Height); + targetImage = disposableImage = this.Image.Clone(x => x.Resize(this.Size.Width, this.Size.Height)); } // Align start/end positions. - Rectangle bounds = this.Image.Bounds; + Rectangle bounds = targetImage.Bounds(); int minX = Math.Max(this.Location.X, sourceRectangle.X); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); maxX = Math.Min(this.Location.X + this.Size.Width, maxX); + int targetX = minX - this.Location.X; int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); @@ -85,9 +84,7 @@ namespace ImageSharp.Drawing.Processors maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; - using (Buffer amount = new Buffer(width)) - using (PixelAccessor toBlendPixels = targetImage.Lock()) - using (PixelAccessor sourcePixels = source.Lock()) + using (var amount = new Buffer(width)) { for (int i = 0; i < width; i++) { @@ -97,11 +94,11 @@ namespace ImageSharp.Drawing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span background = sourcePixels.GetRowSpan(y).Slice(minX, width); - Span foreground = toBlendPixels.GetRowSpan(y - this.Location.Y).Slice(0, width); + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width); this.blender.Blend(background, background, foreground, amount); }); } diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 6eb12cf488..679ca6a228 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -1,21 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using Drawing; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Processors +{ /// /// Using the bursh as a source of pixels colors blends the brush color with source. /// @@ -41,7 +40,7 @@ namespace ImageSharp.Drawing.Processors } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; @@ -67,9 +66,6 @@ namespace ImageSharp.Drawing.Processors int width = maxX - minX; - // We could possibly do some optimization by having knowledge about the individual brushes operate - // for example If brush is SolidBrush then we could just get the color upfront - // and skip using the IBrushApplicator?. using (var amount = new Buffer(width)) using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) { @@ -81,7 +77,7 @@ namespace ImageSharp.Drawing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index c398824323..b6ef4be218 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -1,20 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Drawing.Processors { - using System; - using System.Buffers; - using System.Runtime.CompilerServices; - using Drawing; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - /// /// Usinf a brsuh and a shape fills shape with contents of brush the /// @@ -58,7 +58,7 @@ namespace ImageSharp.Drawing.Processors public GraphicsOptions Options { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Region region = this.Region; Rectangle rect = region.Bounds; @@ -82,8 +82,15 @@ namespace ImageSharp.Drawing.Processors int maxIntersections = region.MaxIntersections; float subpixelCount = 4; + + // we need to offset the pixel grid to account for when we outline a path. + // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5] + // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# + // region to alline with the pixel grid. + float offset = 0.5f; if (this.Options.Antialias) { + offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. subpixelCount = this.Options.AntialiasSubpixelDepth; if (subpixelCount < 4) { @@ -94,7 +101,6 @@ namespace ImageSharp.Drawing.Processors using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { float[] buffer = arrayPool.Rent(maxIntersections); - Span bufferSpan = buffer.AsSpan().Slice(0, maxIntersections); int scanlineWidth = maxX - minX; using (var scanline = new Buffer(scanlineWidth)) { @@ -118,22 +124,22 @@ namespace ImageSharp.Drawing.Processors float subpixelFractionPoint = subpixelFraction / subpixelCount; for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) { - int pointsFound = region.Scan(subPixel, bufferSpan); + int pointsFound = region.Scan(subPixel + offset, buffer, 0); if (pointsFound == 0) { // nothing on this line skip continue; } - QuickSort(bufferSpan.Slice(0, pointsFound)); + QuickSort(new Span(buffer, 0, pointsFound)); for (int point = 0; point < pointsFound; point += 2) { // points will be paired up float scanStart = buffer[point] - minX; float scanEnd = buffer[point + 1] - minX; - int startX = (int)MathF.Floor(scanStart); - int endX = (int)MathF.Floor(scanEnd); + int startX = (int)MathF.Floor(scanStart + offset); + int endX = (int)MathF.Floor(scanEnd + offset); if (startX >= 0 && startX < scanline.Length) { @@ -170,7 +176,7 @@ namespace ImageSharp.Drawing.Processors { for (int x = 0; x < scanlineWidth; x++) { - if (scanline[x] > 0.5) + if (scanline[x] >= 0.5) { scanline[x] = 1; } diff --git a/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs b/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs index fba25a9dba..2891598b9b 100644 --- a/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs +++ b/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// // Common values read from `AssemblyInfo.Common.cs` diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs index 23028b9a8f..c5e7c1cfd4 100644 --- a/src/ImageSharp.Drawing/Region.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using System; - using SixLabors.Primitives; +using System; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing +{ /// /// Represents a region of an image. /// @@ -22,7 +20,7 @@ namespace ImageSharp.Drawing /// Gets the bounding box that entirely surrounds this region. /// /// - /// This should always contains all possible points returned from . + /// This should always contains all possible points returned from . /// public abstract Rectangle Bounds { get; } @@ -31,7 +29,8 @@ namespace ImageSharp.Drawing /// /// The position along the y axis to find intersections. /// The buffer. + /// The point in the buffer to start setting offset. /// The number of intersections found. - public abstract int Scan(float y, Span buffer); + public abstract int Scan(float y, float[] buffer, int offset); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Text/DrawText.Path.cs b/src/ImageSharp.Drawing/Text/DrawText.Path.cs index 523813188a..274b592058 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.Path.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.Path.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Fonts; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.Fonts; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -31,7 +28,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, color, path, TextGraphicsOptions.Default); @@ -50,7 +47,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, Brushes.Solid(color), null, path, options); @@ -68,7 +65,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, path, TextGraphicsOptions.Default); @@ -87,7 +84,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, null, path, options); @@ -105,7 +102,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, pen, path, TextGraphicsOptions.Default); @@ -124,7 +121,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, null, pen, path, options); @@ -143,7 +140,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, pen, path, TextGraphicsOptions.Default); @@ -163,16 +160,11 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { float dpiX = DefaultTextDpi; float dpiY = DefaultTextDpi; - if (options.UseImageResolution) - { - dpiX = (float)source.MetaData.HorizontalResolution; - dpiY = (float)source.MetaData.VerticalResolution; - } var style = new RendererOptions(font, dpiX, dpiY) { diff --git a/src/ImageSharp.Drawing/Text/DrawText.cs b/src/ImageSharp.Drawing/Text/DrawText.cs index 6352836a9c..c0105011e9 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Fonts; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.Fonts; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -34,7 +31,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, color, location, TextGraphicsOptions.Default); @@ -53,7 +50,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, Brushes.Solid(color), null, location, options); @@ -71,7 +68,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default); @@ -90,7 +87,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, null, location, options); @@ -108,7 +105,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default); @@ -127,7 +124,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, null, pen, location, options); @@ -146,7 +143,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default); @@ -166,16 +163,11 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { float dpiX = DefaultTextDpi; float dpiY = DefaultTextDpi; - if (options.UseImageResolution) - { - dpiX = (float)source.MetaData.HorizontalResolution; - dpiY = (float)source.MetaData.VerticalResolution; - } var style = new RendererOptions(font, dpiX, dpiY, location) { diff --git a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs index 593ac36d4f..c230a8c79e 100644 --- a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using ImageSharp.PixelFormats; - using SixLabors.Fonts; +using SixLabors.Fonts; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing +{ /// /// Options for influencing the drawing functions. /// @@ -29,8 +27,6 @@ namespace ImageSharp.Drawing private PixelBlenderMode blenderMode; - private bool? useImageResolution; - private float wrapTextWidth; private SixLabors.Fonts.HorizontalAlignment? horizontalAlignment; @@ -44,7 +40,6 @@ namespace ImageSharp.Drawing { this.applyKerning = true; this.tabWidth = 4; - this.useImageResolution = false; this.wrapTextWidth = 0; this.horizontalAlignment = HorizontalAlignment.Left; this.verticalAlignment = VerticalAlignment.Top; @@ -89,12 +84,6 @@ namespace ImageSharp.Drawing /// public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } - /// - /// Gets or sets a value indicating whether to use the current image resultion to for point size scaling. - /// If this is [false] the text renders at 72dpi otherwise it renders at Image resolution - /// - public bool UseImageResolution { get => this.useImageResolution ?? false; set => this.useImageResolution = value; } - /// /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. /// diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs new file mode 100644 index 0000000000..0acb846c50 --- /dev/null +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -0,0 +1,145 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Extension methods over Image{TPixel} + /// + public static class AdvancedImageExtensions + { + /// + /// Gets the configuration for the image. + /// + /// The Pixel format. + /// The source image + /// Returns the configuration. + public static Configuration GetConfiguration(this Image source) + where TPixel : struct, IPixel + => GetConfiguration((IConfigurable)source); + + /// + /// Returns a reference to the 0th element of the Pixel buffer, + /// allowing direct manipulation of pixel data through unsafe operations. + /// The pixel buffer is a contigous memory area containing Width*Height TPixel elements layed out in row-major order. + /// + /// The Pixel format. + /// The source image frame + /// A pinnable reference the first root of the pixel buffer. + public static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(this ImageFrame source) + where TPixel : struct, IPixel + => ref DangerousGetPinnableReferenceToPixelBuffer((IPixelSource)source); + + /// + /// Returns a reference to the 0th element of the Pixel buffer, + /// allowing direct manipulation of pixel data through unsafe operations. + /// The pixel buffer is a contigous memory area containing Width*Height TPixel elements layed out in row-major order. + /// + /// The Pixel format. + /// The source image + /// A pinnable reference the first root of the pixel buffer. + public static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(this Image source) + where TPixel : struct, IPixel + => ref source.Frames.RootFrame.DangerousGetPinnableReferenceToPixelBuffer(); + + /// + /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. + /// + /// The type of the pixel. + /// The source. + /// The + internal static Span GetPixelSpan(this ImageFrame source) + where TPixel : struct, IPixel + => GetSpan(source); + + /// + /// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// The + internal static Span GetPixelRowSpan(this ImageFrame source, int row) + where TPixel : struct, IPixel + => GetSpan(source, row); + + /// + /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. + /// + /// The type of the pixel. + /// The source. + /// The + internal static Span GetPixelSpan(this Image source) + where TPixel : struct, IPixel + => source.Frames.RootFrame.GetPixelSpan(); + + /// + /// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// The + internal static Span GetPixelRowSpan(this Image source, int row) + where TPixel : struct, IPixel + => source.Frames.RootFrame.GetPixelRowSpan(row); + + /// + /// Gets the span to the backing buffer. + /// + /// The type of the pixel. + /// The source. + /// The span retuned from Pixel source + private static Span GetSpan(IPixelSource source) + where TPixel : struct, IPixel + => source.PixelBuffer.Span; + + /// + /// Gets the span to the backing buffer at the given row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// + /// The span retuned from Pixel source + /// + private static Span GetSpan(IPixelSource source, int row) + where TPixel : struct, IPixel + => GetSpan(source.PixelBuffer, row); + + /// + /// Gets the span to the backing buffer at the given row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// + /// The span retuned from Pixel source + /// + private static Span GetSpan(Buffer2D source, int row) + where TPixel : struct, IPixel + => source.Span.Slice(row * source.Width, source.Width); + + /// + /// Gets the configuration. + /// + /// The source image + /// Returns the bounds of the image + private static Configuration GetConfiguration(IConfigurable source) + => source?.Configuration ?? Configuration.Default; + + /// + /// Returns a reference to the 0th element of the Pixel buffer. + /// Such a reference can be used for pinning but must never be dereferenced. + /// + /// The source image frame + /// A reference to the element. + private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(IPixelSource source) + where TPixel : struct, IPixel + => ref source.PixelBuffer.Span.DangerousGetPinnableReference(); + } +} diff --git a/src/ImageSharp/Advanced/IConfigurable.cs b/src/ImageSharp/Advanced/IConfigurable.cs new file mode 100644 index 0000000000..fd97ae921a --- /dev/null +++ b/src/ImageSharp/Advanced/IConfigurable.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Encapsulates the properties for configuration + /// + internal interface IConfigurable + { + /// + /// Gets the configuration. + /// + Configuration Configuration { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs new file mode 100644 index 0000000000..c9edf118c6 --- /dev/null +++ b/src/ImageSharp/Advanced/IPixelSource.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + /// The type of the pixel. + internal interface IPixelSource + where TPixel : struct, IPixel + { + /// + /// Gets the pixel buffer. + /// + Buffer2D PixelBuffer { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ApplyProcessors.cs b/src/ImageSharp/ApplyProcessors.cs new file mode 100644 index 0000000000..58a952c406 --- /dev/null +++ b/src/ImageSharp/ApplyProcessors.cs @@ -0,0 +1,104 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Mutates the source image by applying the image operation to it. + /// + /// The pixel format. + /// The image to mutate. + /// The operation to perform on the source. + public static void Mutate(this Image source, Action> operation) + where TPixel : struct, IPixel + { + Guard.NotNull(operation, nameof(operation)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); + operation(operationsRunner); + operationsRunner.Apply(); + } + + /// + /// Mutates the source image by applying the operations to it. + /// + /// The pixel format. + /// The image to mutate. + /// The operations to perform on the source. + public static void Mutate(this Image source, params IImageProcessor[] operations) + where TPixel : struct, IPixel + { + Guard.NotNull(operations, nameof(operations)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); + operationsRunner.ApplyProcessors(operations); + operationsRunner.Apply(); + } + + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operation. + /// + /// The pixel format. + /// The image to clone. + /// The operation to perform on the clone. + /// The new + public static Image Clone(this Image source, Action> operation) + where TPixel : struct, IPixel + { + Guard.NotNull(operation, nameof(operation)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); + operation(operationsRunner); + return operationsRunner.Apply(); + } + + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operations. + /// + /// The pixel format. + /// The image to clone. + /// The operations to perform on the clone. + /// The new + public static Image Clone(this Image source, params IImageProcessor[] operations) + where TPixel : struct, IPixel + { + Guard.NotNull(operations, nameof(operations)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); + operationsRunner.ApplyProcessors(operations); + return operationsRunner.Apply(); + } + + /// + /// Applies the given collection against the context + /// + /// The pixel format. + /// The image processing context. + /// The operations to perform on the source. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations) + where TPixel : struct, IPixel + { + foreach (IImageProcessor p in operations) + { + source = source.ApplyProcessor(p); + } + + return source; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs index d382bbedb0..107be4cb2b 100644 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ b/src/ImageSharp/ColorSpaces/CieLab.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents a CIE L*a*b* 1976 color. /// diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 03cf80bf12..834ef56a89 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/src/ImageSharp/ColorSpaces/CieLch.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs index a4e8b424d7..f35914d641 100644 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs index c3fd626e67..9b52517083 100644 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLuv.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International /// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index d9bfe88cd7..d9767d45ea 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents the coordinates of CIEXY chromaticity space /// diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs index 1578f1ac3e..d5ef4b15d3 100644 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/CieXyy.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an CIE xyY 1931 color /// diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index 8d3255e65d..908408000a 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/CieXyz.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an CIE XYZ 1931 color /// diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index eeaef21bd5..2a58a5762a 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs index 29200823e2..2bcdc5127f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Constants use for Cie conversion calculations diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs index acdb356a22..80f9e6789b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using System; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Performs chromatic adaptation on the various color spaces. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 2c274c17a7..9268f3a70c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index e3b3975a4e..13dae4b174 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs index 7f2d184803..cef63e73d2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index dc63e7a67c..04aee4897b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs index 6c4b6ca533..31e1e218ea 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index ca8b31d03d..e6847beafe 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; - using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index 9cfa8f0c3d..637c121ea0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index 9e4a8d9c34..dbc31c52b8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Hsl; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index 80b235894b..640461505b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Hsv; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs index b6d9d4cb98..f5ab4d645c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 5fcc2cd5e7..7b45704afc 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs index 8d888182f9..ac3adee639 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index 1cfd565e80..de13b97eb8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index 6660f579a4..8da7dcb7ef 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs index a52207cb44..f86f505387 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using System.Numerics; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs index aead65e590..c5d91f9a05 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Chromatic adaptation. /// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M] diff --git a/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs b/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs index 920fba18fa..9ef24b38af 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts color between two color spaces. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs index 71b1596cac..0a5ae3627e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs index 8d877503a2..22308260c2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs index 5b63b167e7..35fae30e83 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce +{ /// /// Converts from to . /// @@ -23,7 +21,7 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch // Conversion algorithm described here: // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = MathF.DegreeToRadian(hDegrees); + float hRadians = MathFExtensions.DegreeToRadian(hDegrees); float a = c * MathF.Cos(hRadians); float b = c * MathF.Sin(hRadians); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs index b93dce75a3..aa4614f9ca 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce +{ /// /// Converts from to . /// @@ -25,7 +23,7 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch float l = input.L, a = input.A, b = input.B; float c = MathF.Sqrt((a * a) + (b * b)); float hRadians = MathF.Atan2(b, a); - float hDegrees = MathF.RadianToDegree(hRadians); + float hDegrees = MathFExtensions.RadianToDegree(hRadians); // Wrap the angle round at 360. hDegrees = hDegrees % 360; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs index 3e399016a9..fc6554a905 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce +{ /// /// Converts from to . /// @@ -23,7 +21,7 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv // Conversion algorithm described here: // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = MathF.DegreeToRadian(hDegrees); + float hRadians = MathFExtensions.DegreeToRadian(hDegrees); float u = c * MathF.Cos(hRadians); float v = c * MathF.Sin(hRadians); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs index 5339f1bcda..f0d7a80a22 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce +{ /// /// Converts from to . /// @@ -25,7 +23,7 @@ namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv float l = input.L, a = input.U, b = input.V; float c = MathF.Sqrt((a * a) + (b * b)); float hRadians = MathF.Atan2(b, a); - float hDegrees = MathF.RadianToDegree(hRadians); + float hDegrees = MathFExtensions.RadianToDegree(hRadians); // Wrap the angle round at 360. hDegrees = hDegrees % 360; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs index 36c458828d..50e8335ed6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv -{ - using System.Runtime.CompilerServices; - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs index 3883f3a1ec..709d8d426e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs index dc4a5ccf4a..64fc84b1d4 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce +{ /// /// Color converter between CIE XYZ and CIE xyY /// for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs index b50e89b18c..404bc811ff 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce +{ /// /// Color converter between CMYK and Rgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs index 6c72cf294a..3de3baddd3 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsl -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce +{ /// /// Color converter between HSL and Rgb /// See for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs index 54c8e405f0..6219533ca5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsv -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce +{ /// /// Color converter between HSV and Rgb /// See for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs index 19fc78e9a2..85b0efd16a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +{ /// /// The base class for converting between and color spaces. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs index 7f2df8336e..7faf03c9a1 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +{ /// /// Color converter between CieXyz and HunterLab /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs index af7f4f3708..7e7c536e3f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +{ /// /// Color converter between HunterLab and CieXyz /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs index c856c7ff74..780c9e5a6e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Lms -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce +{ /// /// Color converter between CIE XYZ and LMS /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs index 279044baa7..1bd0c4ad50 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Numerics; // ReSharper disable InconsistentNaming -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Lms +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce { - using System.Numerics; - /// /// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain. /// Used in diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs index c5c6124091..fd76a30fb8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ImageSharp.ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between CieXyz and LinearRgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs index 08200ce6bd..21a80225ee 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements gamma companding /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs index bbf12ca005..132861b476 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements L* companding /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs index 4c46e4d8c7..2ec79b353b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Provides base methods for converting between Rgb and CieXyz color spaces. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs index 4a04185e84..19d4130373 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ImageSharp.ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between LinearRgb and CieXyz /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs index cf4638ae71..29ea0f3148 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between LinearRgb and Rgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs index 0148b91d5f..d279aba850 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System; +using System; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Represents the chromaticity coordinates of RGB primaries. /// One of the specifiers of . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs index 630faedcae..11761f0e4d 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements Rec. 2020 companding function (for 12-bits). /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs index 24dd6ca17b..ccda6bf521 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements the Rec. 709 companding function /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs index 0d0d588288..e40ecc192e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between Rgb and LinearRgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 89e403e761..8a2c66a80c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce { /// /// Trivial implementation of diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs index 18ec94c51d..ce8ea7c6e5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements sRGB companding /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs index e58da580d7..f552acbb48 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce +{ /// /// Color converter between YCbCr and Rgb /// See for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs index 55d1d65a3c..6edae93017 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using System.Numerics; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Basic implementation of the von Kries chromatic adaptation model /// diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs index 5bbbeec308..cf880f1548 100644 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Hsl.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents a Hsl (hue, saturation, lightness) color. /// diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs index dcbb73692b..9f47393792 100644 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Hsv.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index 2b49ee944d..b5ba7c86c7 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/HunterLab.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an Hunter LAB color. /// diff --git a/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs b/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs index a67eaeb064..08c2dafbc6 100644 --- a/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs +++ b/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; +using System; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Defines a generalized method that a value type or class implements to create /// a type-specific method for determining approximate equality of instances. /// /// The type of objects to compare. /// The object specifying the type to specify precision with. - public interface IAlmostEquatable + internal interface IAlmostEquatable where TPrecision : struct, IComparable { /// diff --git a/src/ImageSharp/ColorSpaces/IColorVector.cs b/src/ImageSharp/ColorSpaces/IColorVector.cs index 08b70e4829..85c040b868 100644 --- a/src/ImageSharp/ColorSpaces/IColorVector.cs +++ b/src/ImageSharp/ColorSpaces/IColorVector.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Color represented as a vector in its color space /// - public interface IColorVector + internal interface IColorVector { /// /// Gets the vector representation of the color diff --git a/src/ImageSharp/ColorSpaces/ICompanding.cs b/src/ImageSharp/ColorSpaces/ICompanding.cs index e4f0e4a4c2..2dfa575ed9 100644 --- a/src/ImageSharp/ColorSpaces/ICompanding.cs +++ b/src/ImageSharp/ColorSpaces/ICompanding.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces +namespace SixLabors.ImageSharp.ColorSpaces { /// /// Pair of companding functions for . diff --git a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs index 2360854349..156e94ed3c 100644 --- a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using System; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Encasulates the RGB working color space /// diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs index 224cf99392..85c4063bf4 100644 --- a/src/ImageSharp/ColorSpaces/Illuminants.cs +++ b/src/ImageSharp/ColorSpaces/Illuminants.cs @@ -1,4 +1,7 @@ -namespace ImageSharp.ColorSpaces +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.ColorSpaces { /// /// The well known standard illuminants. diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index fdfc0d2660..07889c3529 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an linear Rgb color with specified working space /// diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs index c76d87253c..82c291de3d 100644 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ b/src/ImageSharp/ColorSpaces/Lms.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// LMS is a color space represented by the response of the three types of cones of the human eye, /// named after their responsivity (sensitivity) at long, medium and short wavelengths. diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index 898c81730a..8ac8411b20 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an RGB color with specified working space /// diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 20b9373948..098ca9a4a4 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; // ReSharper disable InconsistentNaming -namespace ImageSharp.ColorSpaces +namespace SixLabors.ImageSharp.ColorSpaces { - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; - /// /// Chromaticity coordinates taken from: /// diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs index cbba023056..708a74308a 100644 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/YCbCr.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg. /// diff --git a/src/ImageSharp/Common/Constants.cs b/src/ImageSharp/Common/Constants.cs index cf43951bc5..41f2bce247 100644 --- a/src/ImageSharp/Common/Constants.cs +++ b/src/ImageSharp/Common/Constants.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Common constants used throughout the project diff --git a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs index 32a0854359..7a91756b62 100644 --- a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs +++ b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp +{ /// /// The exception that is thrown when the library tries to load /// an image, which has an invalid format. diff --git a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs index ef84a1e393..eb50d0b654 100644 --- a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs +++ b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp +{ /// /// The exception that is thrown when an error occurs when applying a process to an image. /// diff --git a/src/ImageSharp/Common/Extensions/ByteExtensions.cs b/src/ImageSharp/Common/Extensions/ByteExtensions.cs index f1161eb6f6..f6c7207950 100644 --- a/src/ImageSharp/Common/Extensions/ByteExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ByteExtensions.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the struct buffers. /// diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs index d463301025..d6dade7703 100644 --- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for classes that implement . /// @@ -171,19 +169,5 @@ namespace ImageSharp { return (byte)value.Clamp(0, 255); } - - /// - /// Swaps the references to two objects in memory. - /// - /// The first reference. - /// The second reference. - /// The type of object. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Swap(ref T first, ref T second) - { - T temp = second; - second = first; - first = temp; - } } } diff --git a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs index af8e50c7be..e97a2eaed0 100644 --- a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Common +{ /// /// Encapsulates a series of time saving extension methods to the interface. /// - public static class EnumerableExtensions + internal static class EnumerableExtensions { /// /// Generates a sequence of integral numbers within a specified range. diff --git a/src/ImageSharp/Common/Extensions/ListExtensions.cs b/src/ImageSharp/Common/Extensions/ListExtensions.cs new file mode 100644 index 0000000000..2713896c02 --- /dev/null +++ b/src/ImageSharp/Common/Extensions/ListExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Common.Extensions +{ + /// + /// Encapsulates a series of time saving extension methods to the class. + /// + internal static class ListExtensions + { + /// + /// Inserts an item at the given index automatically expanding the capacity if required. + /// + /// The type of object within the list + /// The list + /// The index + /// The item to insert + public static void SafeInsert(this List list, int index, T item) + { + if (index >= list.Count) + { + list.Add(item); + } + else + { + list[index] = item; + } + } + + /// + /// Removes the last element from a list and returns that element. This method changes the length of the list. + /// + /// The type of object within the list + /// The list + /// The last element in the specified sequence. + public static T Pop(this List list) + { + int last = list.Count - 1; + T item = list[last]; + list.RemoveAt(last); + return item; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs new file mode 100644 index 0000000000..0188bc03cf --- /dev/null +++ b/src/ImageSharp/Common/Extensions/SimdUtils.cs @@ -0,0 +1,232 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Various extension and utility methods for and utilizing SIMD capabilities + /// + internal static class SimdUtils + { + /// + /// Gets a value indicating whether the code is being executed on AVX2 CPU where both float and integer registers are of size 256 byte. + /// + public static bool IsAvx2CompatibleArchitecture => Vector.Count == 8 && Vector.Count == 8; + + internal static void GuardAvx2(string operation) + { + if (!IsAvx2CompatibleArchitecture) + { + throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!"); + } + } + + /// + /// Transform all scalars in 'v' in a way that converting them to would have rounding semantics. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 PseudoRound(this Vector4 v) + { + var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1)); + + return v + (sign * 0.5f); + } + + /// + /// Rounds all values in 'v' to the nearest integer following semantics. + /// Source: + /// + /// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110 + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector FastRound(this Vector x) + { + Vector magic0 = new Vector(int.MinValue); // 0x80000000 + Vector sgn0 = Vector.AsVectorSingle(magic0); + Vector and0 = Vector.BitwiseAnd(sgn0, x); + Vector or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); + Vector add0 = Vector.Add(x, or0); + Vector sub0 = Vector.Subtract(add0, or0); + return sub0; + } + + /// + /// Convert 'source.Length' values normalized into [0..1] from 'source' into 'dest' buffer of values. + /// The values gonna be scaled up into [0-255] and rounded. + /// Based on: + /// + /// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions + /// + /// + internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan source, Span dest) + { + GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); + + DebugGuard.IsTrue((source.Length % Vector.Count) == 0, nameof(source), "source.Length should be divisable by Vector.Count!"); + + if (source.Length == 0) + { + return; + } + + ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); + ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + int n = source.Length / 8; + + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + // need to copy to a temporal struct, because + // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) + // does not work. TODO: This might be a CoreClr bug, need to ask/report + var temp = default(Octet.OfUInt32); + ref Vector tempRef = ref Unsafe.As>(ref temp); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + Vector x = Unsafe.Add(ref srcBase, i); + x = (x * scale) + magick; + tempRef = x; + + ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); + d.LoadFrom(ref temp); + } + } + + /// + /// Same as but clamps overflown values before conversion. + /// + internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest) + { + GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); + + DebugGuard.IsTrue((source.Length % Vector.Count) == 0, nameof(source), "source.Length should be divisable by Vector.Count!"); + + if (source.Length == 0) + { + return; + } + + ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); + ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + int n = source.Length / 8; + + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + // need to copy to a temporal struct, because + // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) + // does not work. TODO: This might be a CoreClr bug, need to ask/report + var temp = default(Octet.OfUInt32); + ref Vector tempRef = ref Unsafe.As>(ref temp); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + Vector x = Unsafe.Add(ref srcBase, i); + x = Vector.Max(x, Vector.Zero); + x = Vector.Min(x, Vector.One); + + x = (x * scale) + magick; + tempRef = x; + + ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); + d.LoadFrom(ref temp); + } + } + + // TODO: Replace these with T4-d library level tuples! + internal static class Octet + { + [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] + public struct OfUInt32 + { + [FieldOffset(0 * sizeof(uint))] + public uint V0; + + [FieldOffset(1 * sizeof(uint))] + public uint V1; + + [FieldOffset(2 * sizeof(uint))] + public uint V2; + + [FieldOffset(3 * sizeof(uint))] + public uint V3; + + [FieldOffset(4 * sizeof(uint))] + public uint V4; + + [FieldOffset(5 * sizeof(uint))] + public uint V5; + + [FieldOffset(6 * sizeof(uint))] + public uint V6; + + [FieldOffset(7 * sizeof(uint))] + public uint V7; + + public override string ToString() + { + return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; + } + } + + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct OfByte + { + [FieldOffset(0)] + public byte V0; + + [FieldOffset(1)] + public byte V1; + + [FieldOffset(2)] + public byte V2; + + [FieldOffset(3)] + public byte V3; + + [FieldOffset(4)] + public byte V4; + + [FieldOffset(5)] + public byte V5; + + [FieldOffset(6)] + public byte V6; + + [FieldOffset(7)] + public byte V7; + + public override string ToString() + { + return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; + } + + public void LoadFrom(ref OfUInt32 i) + { + this.V0 = (byte)i.V0; + this.V1 = (byte)i.V1; + this.V2 = (byte)i.V2; + this.V3 = (byte)i.V3; + this.V4 = (byte)i.V4; + this.V5 = (byte)i.V5; + this.V6 = (byte)i.V6; + this.V7 = (byte)i.V7; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index 6de94dd229..b717abab1c 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Buffers; - using System.IO; +using System.Buffers; +using System.IO; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -27,14 +25,23 @@ namespace ImageSharp if (stream.CanSeek) { - stream.Position += count; + stream.Seek(count, SeekOrigin.Current); // Position += count; } else { byte[] foo = ArrayPool.Shared.Rent(count); try { - stream.Read(foo, 0, count); + while (count > 0) + { + int bytesRead = stream.Read(foo, 0, count); + if (bytesRead == 0) + { + break; + } + + count -= bytesRead; + } } finally { diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs index fac33da140..8133ebb38e 100644 --- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs +++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs @@ -1,19 +1,45 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the struct. /// internal static class Vector4Extensions { + /// + /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact. + /// + /// The to premultiply + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Premultiply(this Vector4 source) + { + float w = source.W; + Vector4 premultiplied = source * w; + premultiplied.W = w; + return premultiplied; + } + + /// + /// Reverses the result of premultiplying a vector via . + /// + /// The to premultiply + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 UnPremultiply(this Vector4 source) + { + float w = source.W; + Vector4 unpremultiplied = source / w; + unpremultiplied.W = w; + return unpremultiplied; + } + /// /// Compresses a linear color signal to its sRGB equivalent. /// diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index f6941fc6fc..dc3cff7a2b 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Diagnostics; +using System; +using System.Diagnostics; +namespace SixLabors.ImageSharp +{ /// /// Provides methods to protect against invalid parameters for a DEBUG build. /// diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 8e55c18af8..b0546bf9a9 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +namespace SixLabors.ImageSharp +{ /// /// Provides methods to protect against invalid parameters. /// diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 367aa12d00..75c9190d24 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Provides common mathematical methods. /// @@ -67,6 +64,27 @@ namespace ImageSharp return left * right; } + /// + /// Returns the result of a normalized sine cardinal function for the given value. + /// SinC(x) = sin(pi*x)/(pi*x). + /// + /// A single-precision floating-point number to calculate the result for. + /// + /// The sine cardinal of . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float SinC(float f) + { + if (MathF.Abs(f) > Constants.Epsilon) + { + f *= MathF.PI; + float result = MathF.Sin(f) / f; + return MathF.Abs(result) < Constants.Epsilon ? 0F : result; + } + + return 1F; + } + /// /// Returns the result of a B-C filter against the given value. /// @@ -121,27 +139,6 @@ namespace ImageSharp return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); } - /// - /// Gets the bounding from the given matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// - /// The . - /// - public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) - { - Vector2 leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); - Vector2 rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); - Vector2 leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); - Vector2 rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); - - Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom }; - float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min(); - float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min(); - return new Rectangle(0, 0, (int)extentX, (int)extentY); - } - /// /// Finds the bounding rectangle based on the first instance of any color component other /// than the given one. @@ -153,7 +150,7 @@ namespace ImageSharp /// /// The . /// - public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) + public static Rectangle GetFilteredBoundingRectangle(ImageFrame bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) where TPixel : struct, IPixel { int width = bitmap.Width; @@ -161,7 +158,7 @@ namespace ImageSharp var topLeft = default(Point); var bottomRight = default(Point); - Func, int, int, float, bool> delegateFunc; + Func, int, int, float, bool> delegateFunc; // Determine which channel to check against switch (channel) @@ -183,7 +180,7 @@ namespace ImageSharp break; } - int GetMinY(ImageBase pixels) + int GetMinY(ImageFrame pixels) { for (int y = 0; y < height; y++) { @@ -199,7 +196,7 @@ namespace ImageSharp return 0; } - int GetMaxY(ImageBase pixels) + int GetMaxY(ImageFrame pixels) { for (int y = height - 1; y > -1; y--) { @@ -215,7 +212,7 @@ namespace ImageSharp return height; } - int GetMinX(ImageBase pixels) + int GetMinX(ImageFrame pixels) { for (int x = 0; x < width; x++) { @@ -231,7 +228,7 @@ namespace ImageSharp return 0; } - int GetMaxX(ImageBase pixels) + int GetMaxX(ImageFrame pixels) { for (int x = width - 1; x > -1; x--) { diff --git a/src/ImageSharp/Common/Helpers/MathF.cs b/src/ImageSharp/Common/Helpers/MathF.cs deleted file mode 100644 index 5ce5b5d5dd..0000000000 --- a/src/ImageSharp/Common/Helpers/MathF.cs +++ /dev/null @@ -1,293 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; - - /// - /// Provides single-precision floating point constants and static methods for trigonometric, logarithmic, and other common mathematical functions. - /// - // ReSharper disable InconsistentNaming - internal static class MathF - { - /// - /// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π. - /// - public const float PI = (float)Math.PI; - - /// - /// Returns the absolute value of a single-precision floating-point number. - /// - /// - /// A number that is greater than or equal to , but less than or equal to . - /// - /// - /// A single-precision floating-point number, x, such that 0 ≤ x ≤. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Abs(float f) - { - return Math.Abs(f); - } - - /// - /// Returns the angle whose tangent is the quotient of two specified numbers. - /// - /// The y coordinate of a point. - /// The x coordinate of a point. - /// - /// An angle, θ, measured in radians, such that -π≤θ≤π, and tan(θ) = y / x, where - /// (x, y) is a point in the Cartesian plane. Observe the following: For (x, y) in - /// quadrant 1, 0 < θ < π/2.For (x, y) in quadrant 2, π/2 < θ≤π.For (x, y) in quadrant - /// 3, -π < θ < -π/2.For (x, y) in quadrant 4, -π/2 < θ < 0.For points on the boundaries - /// of the quadrants, the return value is the following:If y is 0 and x is not negative, - /// θ = 0.If y is 0 and x is negative, θ = π.If y is positive and x is 0, θ = π/2.If - /// y is negative and x is 0, θ = -π/2.If y is 0 and x is 0, θ = 0. If x or y is - /// , or if x and y are either or - /// , the method returns . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Atan2(float y, float x) - { - return (float)Math.Atan2(y, x); - } - - /// - /// Returns the smallest integral value that is greater than or equal to the specified single-precision floating-point number. - /// - /// A single-precision floating-point number. - /// - /// The smallest integral value that is greater than or equal to . - /// If is equal to , , - /// or , that value is returned. - /// Note that this method returns a instead of an integral type. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Ceiling(float f) - { - return (float)Math.Ceiling(f); - } - - /// - /// Returns the cosine of the specified angle. - /// - /// An angle, measured in radians. - /// - /// The cosine of . If is equal to , , - /// or , this method returns . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Cos(float f) - { - return (float)Math.Cos(f); - } - - /// - /// Converts a degree (360-periodic) angle to a radian (2*Pi-periodic) angle. - /// - /// The angle in degrees. - /// - /// The representing the degree as radians. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DegreeToRadian(float degree) - { - return degree * (PI / 180F); - } - - /// - /// Returns e raised to the specified power. - /// - /// A number specifying a power. - /// - /// The number e raised to the power . - /// If equals or , that value is returned. - /// If equals , 0 is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Exp(float f) - { - return (float)Math.Exp(f); - } - - /// - /// Returns the largest integer less than or equal to the specified single-precision floating-point number. - /// - /// A single-precision floating-point number. - /// - /// The largest integer less than or equal to . - /// If is equal to , , - /// or , that value is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Floor(float f) - { - return (float)Math.Floor(f); - } - - /// - /// Returns the larger of two single-precision floating-point numbers. - /// - /// The first of two single-precision floating-point numbers to compare. - /// The second of two single-precision floating-point numbers to compare. - /// - /// Parameter or , whichever is larger. - /// If , or , or both and are - /// equal to , is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Max(float val1, float val2) - { - return Math.Max(val1, val2); - } - - /// - /// Returns the smaller of two single-precision floating-point numbers. - /// - /// The first of two single-precision floating-point numbers to compare. - /// The second of two single-precision floating-point numbers to compare. - /// - /// Parameter or , whichever is smaller. - /// If , , or both and are equal - /// to , is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Min(float val1, float val2) - { - return Math.Min(val1, val2); - } - - /// - /// Returns a specified number raised to the specified power. - /// - /// A single-precision floating-point number to be raised to a power. - /// A single-precision floating-point number that specifies a power. - /// The number raised to the power . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Pow(float x, float y) - { - return (float)Math.Pow(x, y); - } - - /// - /// Converts a radian (2*Pi-periodic) angle to a degree (360-periodic) angle. - /// - /// The angle in radians. - /// - /// The representing the degree as radians. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float RadianToDegree(float radian) - { - return radian / (PI / 180F); - } - - /// - /// Rounds a single-precision floating-point value to the nearest integral value. - /// - /// A single-precision floating-point number to be rounded. - /// - /// The integer nearest . - /// If the fractional component of is halfway between two integers, one of which is even and the other odd, then the even number is returned. - /// Note that this method returns a instead of an integral type. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Round(float f) - { - return (float)Math.Round(f); - } - - /// - /// Rounds a single-precision floating-point value to the nearest integer. - /// A parameter specifies how to round the value if it is midway between two numbers. - /// - /// A single-precision floating-point number to be rounded. - /// Specification for how to round if it is midway between two other numbers. - /// - /// The integer nearest . If is halfway between two integers, one of which is even - /// and the other odd, then determines which of the two is returned. - /// Note that this method returns a instead of an integral type. - /// - /// - /// is not a valid value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Round(float f, MidpointRounding mode) - { - return (float)Math.Round(f, mode); - } - - /// - /// Returns the sine of the specified angle. - /// - /// An angle, measured in radians. - /// - /// The sine of . - /// If is equal to , , - /// or , this method returns . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sin(float f) - { - return (float)Math.Sin(f); - } - - /// - /// Returns the result of a normalized sine cardinal function for the given value. - /// SinC(x) = sin(pi*x)/(pi*x). - /// - /// A single-precision floating-point number to calculate the result for. - /// - /// The sine cardinal of . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float SinC(float f) - { - if (Abs(f) > Constants.Epsilon) - { - f *= PI; - return Clean(Sin(f) / f); - } - - return 1F; - } - - /// - /// Returns the square root of a specified number. - /// - /// The number whose square root is to be found. - /// - /// One of the values in the following table. - /// parameter Return value Zero or positive The positive square root of . - /// Negative Equals - /// Equals - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Sqrt(float f) - { - return (float)Math.Sqrt(f); - } - - /// - /// Ensures that any passed float is correctly rounded to zero - /// - /// The value to clean. - /// - /// The - /// . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Clean(float x) - { - if (Abs(x) < Constants.Epsilon) - { - return 0F; - } - - return x; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Tuples/Tuple8.cs b/src/ImageSharp/Common/Tuples/Tuple8.cs new file mode 100644 index 0000000000..3335e6e377 --- /dev/null +++ b/src/ImageSharp/Common/Tuples/Tuple8.cs @@ -0,0 +1,98 @@ +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Common.Tuples +{ + /// + /// Contains value type tuples of 8 elements. + /// TODO: We should T4 this stuff to be DRY + /// + internal static class Tuple8 + { + /// + /// Value type tuple of 8 -s + /// + [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] + public struct OfUInt32 + { + [FieldOffset(0 * sizeof(uint))] + public uint V0; + + [FieldOffset(1 * sizeof(uint))] + public uint V1; + + [FieldOffset(2 * sizeof(uint))] + public uint V2; + + [FieldOffset(3 * sizeof(uint))] + public uint V3; + + [FieldOffset(4 * sizeof(uint))] + public uint V4; + + [FieldOffset(5 * sizeof(uint))] + public uint V5; + + [FieldOffset(6 * sizeof(uint))] + public uint V6; + + [FieldOffset(7 * sizeof(uint))] + public uint V7; + + public override string ToString() + { + return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; + } + } + + /// + /// Value type tuple of 8 -s + /// + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct OfByte + { + [FieldOffset(0)] + public byte V0; + + [FieldOffset(1)] + public byte V1; + + [FieldOffset(2)] + public byte V2; + + [FieldOffset(3)] + public byte V3; + + [FieldOffset(4)] + public byte V4; + + [FieldOffset(5)] + public byte V5; + + [FieldOffset(6)] + public byte V6; + + [FieldOffset(7)] + public byte V7; + + public override string ToString() + { + return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; + } + + /// + /// Sets the values of this tuple by casting all elements of the given tuple to . + /// + public void LoadFrom(ref OfUInt32 i) + { + this.V0 = (byte)i.V0; + this.V1 = (byte)i.V1; + this.V2 = (byte)i.V2; + this.V3 = (byte)i.V3; + this.V4 = (byte)i.V4; + this.V5 = (byte)i.V5; + this.V6 = (byte)i.V6; + this.V7 = (byte)i.V7; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs new file mode 100644 index 0000000000..309d5e2e56 --- /dev/null +++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs @@ -0,0 +1,80 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Common.Tuples +{ + /// + /// Its faster to process multiple Vector4-s together, so let's pair them! + /// On AVX2 this pair should be convertible to of ! + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Vector4Pair + { + public Vector4 A; + + public Vector4 B; + + private static readonly Vector4 Scale = new Vector4(1 / 255f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MultiplyInplace(float value) + { + this.A *= value; + this.B *= value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddInplace(Vector4 value) + { + this.A += value; + this.B += value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddInplace(ref Vector4Pair other) + { + this.A += other.A; + this.B += other.B; + } + + /// + /// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! + /// TODO: Move it somewhere else. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void RoundAndDownscalePreAvx2() + { + ref Vector a = ref Unsafe.As>(ref this.A); + a = a.FastRound(); + + ref Vector b = ref Unsafe.As>(ref this.B); + b = b.FastRound(); + + // Downscale by 1/255 + this.A *= Scale; + this.B *= Scale; + } + + /// + /// AVX2-only Downscale method, specific to Jpeg color conversion. + /// TODO: Move it somewhere else. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void RoundAndDownscaleAvx2() + { + ref Vector self = ref Unsafe.As>(ref this); + Vector v = self; + v = v.FastRound(); + + // Downscale by 1/255 + v *= new Vector(1 / 255f); + self = v; + } + + public override string ToString() + { + return $"{this.A}, {this.B}"; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 226d451325..7401035331 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -1,19 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - - using Formats; - using ImageSharp.IO; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.IO; +namespace SixLabors.ImageSharp +{ /// /// Provides initialization code which allows extending the library. /// @@ -35,14 +36,14 @@ namespace ImageSharp private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); /// - /// The list of supported s. + /// The list of supported s. /// - private readonly List imageFormatDetectors = new List(); + private readonly ConcurrentBag imageFormats = new ConcurrentBag(); /// - /// The list of supported s. + /// The list of supported s. /// - private readonly HashSet imageFormats = new HashSet(); + private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); /// /// Initializes a new instance of the class. @@ -76,6 +77,11 @@ namespace ImageSharp /// public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.imageFormats; + /// /// Gets the maximum header size of all the formats. /// @@ -96,18 +102,18 @@ namespace ImageSharp /// internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; - /// - /// Gets the currently registered s. - /// - internal IEnumerable ImageFormats => this.imageFormats; - #if !NETSTANDARD1_1 /// - /// Gets or sets the fielsystem helper for accessing the local file system. + /// Gets or sets the filesystem helper for accessing the local file system. /// internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); #endif + /// + /// Gets or sets the image operations provider factory. + /// + internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); + /// /// Registers a new format provider. /// @@ -121,7 +127,7 @@ namespace ImageSharp /// /// Registers a new format provider. /// - /// The format to register as a well know format. + /// The format to register as a known format. public void AddImageFormat(IImageFormat format) { Guard.NotNull(format, nameof(format)); @@ -135,7 +141,7 @@ namespace ImageSharp /// /// The extension to discover /// The if found otherwise null - public IImageFormat FindFormatByFileExtensions(string extension) + public IImageFormat FindFormatByFileExtension(string extension) { return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); } @@ -144,7 +150,7 @@ namespace ImageSharp /// For the specified mime type find the . /// /// The mime-type to discover - /// The if found otherwise null + /// The if found; otherwise null public IImageFormat FindFormatByMimeType(string mimeType) { return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); @@ -181,7 +187,7 @@ namespace ImageSharp /// public void ClearImageFormatDetectors() { - this.imageFormatDetectors.Clear(); + this.imageFormatDetectors = new ConcurrentBag(); } /// @@ -195,29 +201,12 @@ namespace ImageSharp this.SetMaxHeaderSize(); } - /// - /// Creates the default instance with the following s preregistered: - /// - /// - /// - /// - /// - /// The default configuration of - internal static Configuration CreateDefaultInstance() - { - return new Configuration( - new PngConfigurationModule(), - new JpegConfigurationModule(), - new GifConfigurationModule(), - new BmpConfigurationModule()); - } - /// /// For the specified mime type find the decoder. /// /// The format to discover /// The if found otherwise null - internal IImageDecoder FindDecoder(IImageFormat format) + public IImageDecoder FindDecoder(IImageFormat format) { Guard.NotNull(format, nameof(format)); if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) @@ -233,7 +222,7 @@ namespace ImageSharp /// /// The format to discover /// The if found otherwise null - internal IImageEncoder FindEncoder(IImageFormat format) + public IImageEncoder FindEncoder(IImageFormat format) { Guard.NotNull(format, nameof(format)); if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) @@ -244,6 +233,23 @@ namespace ImageSharp return null; } + /// + /// Creates the default instance with the following s preregistered: + /// + /// + /// + /// + /// + /// The default configuration of + internal static Configuration CreateDefaultInstance() + { + return new Configuration( + new PngConfigurationModule(), + new JpegConfigurationModule(), + new GifConfigurationModule(), + new BmpConfigurationModule()); + } + /// /// Sets the max header size. /// diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs new file mode 100644 index 0000000000..6e6feed84e --- /dev/null +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Performs processor application operations on the source image + /// + /// The pixel format + internal class DefaultInternalImageProcessorContext : IInternalImageProcessingContext + where TPixel : struct, IPixel + { + private readonly bool mutate; + private readonly Image source; + private Image destination; + + /// + /// Initializes a new instance of the class. + /// + /// The image. + /// The mutate. + public DefaultInternalImageProcessorContext(Image source, bool mutate) + { + this.mutate = mutate; + this.source = source; + if (this.mutate) + { + this.destination = source; + } + } + + /// + public Image Apply() + { + if (!this.mutate && this.destination == null) + { + // Ensure we have cloned it if we are not mutating as we might have failed to register any processors + this.destination = this.source.Clone(); + } + + return this.destination; + } + + /// + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + if (!this.mutate && this.destination == null) + { + // This will only work if the first processor applied is the cloning one thus + // realistically for this optimization to work the resize must the first processor + // applied any only up processors will take the double data path. + if (processor is ICloningImageProcessor cloningImageProcessor) + { + this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); + return this; + } + + this.destination = this.source.Clone(); + } + + processor.Apply(this.destination, rectangle); + return this; + } + + /// + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + return this.ApplyProcessor(processor, this.source.Bounds()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs similarity index 72% rename from src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs index 629944ea15..3899b14cc9 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Atkinson image dithering algorithm. /// /// - public sealed class Atkinson : ErrorDiffuser + public sealed class AtkinsonDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Atkinson() + public AtkinsonDiffuser() : base(AtkinsonMatrix, 8) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs similarity index 65% rename from src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs index 02d41c369e..4d9f4d3c4f 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Burks image dithering algorithm. /// /// - public sealed class Burks : ErrorDiffuser + public sealed class BurksDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Burks() + public BurksDiffuser() : base(BurksMatrix, 32) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs similarity index 74% rename from src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs index 408e6c383e..46bafcc0cf 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs @@ -1,21 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering.Base +{ /// /// The base class for performing error diffusion based dithering. /// - public abstract class ErrorDiffuser : IErrorDiffuser + public abstract class ErrorDiffuserBase : IErrorDiffuser { /// /// The vector to perform division. @@ -43,11 +41,11 @@ namespace ImageSharp.Dithering private readonly Fast2DArray matrix; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The dithering matrix. /// The divisor. - internal ErrorDiffuser(Fast2DArray matrix, byte divisor) + internal ErrorDiffuserBase(Fast2DArray matrix, byte divisor) { Guard.NotNull(matrix, nameof(matrix)); Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); @@ -60,7 +58,7 @@ namespace ImageSharp.Dithering this.startingOffset = 0; for (int i = 0; i < this.matrixWidth; i++) { - // Good to disable here as we are not comparing matematical output. + // Good to disable here as we are not comparing mathematical output. // ReSharper disable once CompareOfFloatsByEqualityOperator if (matrix[0, i] != 0) { @@ -72,15 +70,15 @@ namespace ImageSharp.Dithering /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageBase pixels, TPixel source, TPixel transformed, int x, int y, int width, int height) + public void Dither(ImageFrame pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel { - this.Dither(pixels, source, transformed, x, y, width, height, true); + this.Dither(pixels, source, transformed, x, y, minX, minY, maxX, maxY, true); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel) + public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) where TPixel : struct, IPixel { if (replacePixel) @@ -92,19 +90,19 @@ namespace ImageSharp.Dithering // Calculate the error Vector4 error = source.ToVector4() - transformed.ToVector4(); - // Loop through and distribute the error amongst neighbouring pixels. + // Loop through and distribute the error amongst neighboring pixels. for (int row = 0; row < this.matrixHeight; row++) { int matrixY = y + row; - if (matrixY > 0 && matrixY < height) + if (matrixY > minY && matrixY < maxY) { - Span rowSpan = image.GetRowSpan(matrixY); + Span rowSpan = image.GetPixelRowSpan(matrixY); for (int col = 0; col < this.matrixWidth; col++) { int matrixX = x + (col - this.startingOffset); - if (matrixX > 0 && matrixX < width) + if (matrixX > minX && matrixX < maxX) { float coefficient = this.matrix[row, col]; @@ -117,10 +115,8 @@ namespace ImageSharp.Dithering ref TPixel pixel = ref rowSpan[matrixX]; var offsetColor = pixel.ToVector4(); - var coefficientVector = new Vector4(coefficient); - Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; - result.W = offsetColor.W; + Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; pixel.PackFromVector4(result); } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs similarity index 70% rename from src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs index 6165da6344..6457fbe01e 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm. /// /// - public sealed class FloydSteinberg : ErrorDiffuser + public sealed class FloydSteinbergDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public FloydSteinberg() + public FloydSteinbergDiffuser() : base(FloydSteinbergMatrix, 16) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index bc785e8971..c538d643c6 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Encapsulates properties and methods required to perfom diffused error dithering on an image. /// @@ -20,10 +18,12 @@ namespace ImageSharp.Dithering /// The transformed pixel /// The column index. /// The row index. - /// The image width. - /// The image height. + /// The minimum column value. + /// The minimum row value. + /// The maximum column value. + /// The maximum row value. /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int width, int height) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel; /// @@ -34,14 +34,16 @@ namespace ImageSharp.Dithering /// The transformed pixel /// The column index. /// The row index. - /// The image width. - /// The image height. + /// The minimum column value. + /// The minimum row value. + /// The maximum column value. + /// The maximum row value. /// /// Whether to replace the pixel at the given coordinates with the transformed value. /// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false. /// /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs similarity index 70% rename from src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs index 6daeab32f7..30e09b47a9 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm. /// /// - public sealed class JarvisJudiceNinke : ErrorDiffuser + public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public JarvisJudiceNinke() + public JarvisJudiceNinkeDiffuser() : base(JarvisJudiceNinkeMatrix, 48) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs similarity index 71% rename from src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs index 0c0944d0eb..c472d25b0d 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Sierra2 image dithering algorithm. /// /// - public sealed class Sierra2 : ErrorDiffuser + public sealed class Sierra2Diffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Sierra2() + public Sierra2Diffuser() : base(Sierra2Matrix, 16) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs similarity index 72% rename from src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs index 2e22208462..c19ab2aaac 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Sierra3 image dithering algorithm. /// /// - public sealed class Sierra3 : ErrorDiffuser + public sealed class Sierra3Diffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Sierra3() + public Sierra3Diffuser() : base(Sierra3Matrix, 32) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs similarity index 70% rename from src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs index fe4c933a9f..263bae568a 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the SierraLite image dithering algorithm. /// /// - public sealed class SierraLite : ErrorDiffuser + public sealed class SierraLiteDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public SierraLite() + public SierraLiteDiffuser() : base(SierraLiteMatrix, 4) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs similarity index 66% rename from src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs index b04c164814..0717695065 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Stucki image dithering algorithm. /// /// - public sealed class Stucki : ErrorDiffuser + public sealed class StuckiDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Stucki() + public StuckiDiffuser() : base(StuckiMatrix, 42) { } diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/BayerDither.cs similarity index 70% rename from src/ImageSharp/Dithering/Ordered/Bayer.cs rename to src/ImageSharp/Dithering/Ordered/BayerDither.cs index ded17d1e10..685dca5fe8 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/BayerDither.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering.Ordered -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix. /// /// - public sealed class Bayer : OrderedDither4x4 + public sealed class BayerDither : OrderedDitherBase { /// /// The threshold matrix. @@ -27,9 +26,9 @@ namespace ImageSharp.Dithering.Ordered }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Bayer() + public BayerDither() : base(ThresholdMatrix) { } diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index c69cddefed..689c9a85b4 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Encapsulates properties and methods required to perfom ordered dithering on an image. /// @@ -19,14 +17,12 @@ namespace ImageSharp.Dithering /// The source pixel /// The color to apply to the pixels above the threshold. /// The color to apply to the pixels below the threshold. - /// The byte array to pack/unpack to. Must have a length of 4. Bytes are unpacked to Xyzw order. + /// The to pack/unpack to. /// The component index to test the threshold against. Must range from 0 to 3. /// The column index. /// The row index. - /// The image width. - /// The image height. /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height) + void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs similarity index 74% rename from src/ImageSharp/Dithering/Ordered/Ordered.cs rename to src/ImageSharp/Dithering/Ordered/OrderedDither.cs index 1fd39eb8ba..12968914d0 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering.Ordered -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the 4x4 ordered dithering matrix. /// /// - public sealed class Ordered : OrderedDither4x4 + public sealed class OrderedDither : OrderedDitherBase { /// /// The threshold matrix. @@ -27,9 +26,9 @@ namespace ImageSharp.Dithering.Ordered }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Ordered() + public OrderedDither() : base(ThresholdMatrix) { } diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs deleted file mode 100644 index a180888f70..0000000000 --- a/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Dithering.Ordered -{ - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// The base class for performing ordered ditheroing using a 4x4 matrix. - /// - public abstract class OrderedDither4x4 : IOrderedDither - { - /// - /// The dithering matrix - /// - private Fast2DArray matrix; - - /// - /// Initializes a new instance of the class. - /// - /// The thresholding matrix. - internal OrderedDither4x4(Fast2DArray matrix) - { - this.matrix = matrix; - } - - /// - public void Dither(ImageBase image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height) - where TPixel : struct, IPixel - { - // TODO: This doesn't really cut it for me. - // I'd rather be using float but we need to add some sort of normalization vector methods to all IPixel implementations - // before we can do that as the vectors all cover different ranges. - source.ToXyzwBytes(bytes, 0); - image[x, y] = this.matrix[y % 3, x % 3] >= bytes[index] ? lower : upper; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs new file mode 100644 index 0000000000..818a24d5dd --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Dithering.Base +{ + /// + /// The base class for performing ordered dithering using a 4x4 matrix. + /// + public abstract class OrderedDitherBase : IOrderedDither + { + /// + /// The dithering matrix + /// + private Fast2DArray matrix; + + /// + /// Initializes a new instance of the class. + /// + /// The thresholding matrix. + internal OrderedDitherBase(Fast2DArray matrix) + { + this.matrix = matrix; + } + + /// + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) + where TPixel : struct, IPixel + { + source.ToRgba32(ref rgba); + switch (index) + { + case 0: + image[x, y] = this.matrix[y % 3, x % 3] >= rgba.R ? lower : upper; + return; + case 1: + image[x, y] = this.matrix[y % 3, x % 3] >= rgba.G ? lower : upper; + return; + case 2: + image[x, y] = this.matrix[y % 3, x % 3] >= rgba.B ? lower : upper; + return; + case 3: + image[x, y] = this.matrix[y % 3, x % 3] >= rgba.A ? lower : upper; + return; + } + + throw new ArgumentOutOfRangeException(nameof(index), "Index should be between 0 and 3 inclusive."); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 330326acfc..d08487cf27 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Enumerates the available bits per pixel for bitmap. diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index a9246d449e..1280498acb 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Defines how the compression type of the image data diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs index f70ff1a56d..b091467bf5 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Registers the image encoders, decoders and mime type detectors for the bmp format. @@ -13,8 +11,8 @@ namespace ImageSharp.Formats /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Bitmap, new BmpEncoder()); - config.SetDecoder(ImageFormats.Bitmap, new BmpDecoder()); + config.SetEncoder(ImageFormats.Bmp, new BmpEncoder()); + config.SetDecoder(ImageFormats.Bmp, new BmpDecoder()); config.AddImageFormatDetector(new BmpImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs index d394b61f6e..b7291bb99e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Defines constants relating to BMPs /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 5baf1b1a5a..78a9de6c45 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -1,16 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image decoder for generating an image out of a Windows bitmap stream. /// @@ -26,7 +21,7 @@ namespace ImageSharp.Formats /// Formats will be supported in a later releases. We advise always /// to use only 24 Bit Windows bitmaps. /// - public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions + public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector { /// public Image Decode(Configuration configuration, Stream stream) @@ -37,5 +32,13 @@ namespace ImageSharp.Formats return new BmpDecoderCore(configuration, this).Decode(stream); } + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + return new BmpDecoderCore(configuration, this).Identify(stream); + } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 817d00f7e7..e552ac1042 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1,34 +1,57 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.IO; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Performs the bmp decoding operation. /// + /// + /// A useful decoding source example can be found at + /// internal sealed class BmpDecoderCore { /// /// The mask for the red part of the color for 16 bit rgb bitmaps. /// - private const int Rgb16RMask = 0x00007C00; + private const int Rgb16RMask = 0x7C00; /// /// The mask for the green part of the color for 16 bit rgb bitmaps. /// - private const int Rgb16GMask = 0x000003E0; + private const int Rgb16GMask = 0x3E0; /// /// The mask for the blue part of the color for 16 bit rgb bitmaps. /// - private const int Rgb16BMask = 0x0000001F; + private const int Rgb16BMask = 0x1F; + + /// + /// RLE8 flag value that indicates following byte has special meaning + /// + private const int RleCommand = 0x00; + + /// + /// RLE8 flag value marking end of a scan line + /// + private const int RleEndOfLine = 0x00; + + /// + /// RLE8 flag value marking end of bitmap data + /// + private const int RleEndOfBitmap = 0x01; + + /// + /// RLE8 flag value marking the start of [x,y] offset instruction + /// + private const int RleDelta = 0x02; /// /// The stream to decode from. @@ -72,74 +95,16 @@ namespace ImageSharp.Formats public Image Decode(Stream stream) where TPixel : struct, IPixel { - this.currentStream = stream; - try { - this.ReadFileHeader(); - this.ReadInfoHeader(); - - // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 - // If the height is negative, then this is a Windows bitmap whose origin - // is the upper-left corner and not the lower-left.The inverted flag - // indicates a lower-left origin.Our code will be outputting an - // upper-left origin pixel array. - bool inverted = false; - if (this.infoHeader.Height < 0) - { - inverted = true; - this.infoHeader.Height = -this.infoHeader.Height; - } - - int colorMapSize = -1; - - if (this.infoHeader.ClrUsed == 0) - { - if (this.infoHeader.BitsPerPixel == 1 || - this.infoHeader.BitsPerPixel == 4 || - this.infoHeader.BitsPerPixel == 8) - { - colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4; - } - } - else - { - colorMapSize = this.infoHeader.ClrUsed * 4; - } - - byte[] palette = null; + this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); - if (colorMapSize > 0) - { - // 256 * 4 - if (colorMapSize > 1024) - { - throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); - } - - palette = new byte[colorMapSize]; - - this.currentStream.Read(palette, 0, colorMapSize); - } - - if (this.infoHeader.Width > Image.MaxWidth || this.infoHeader.Height > Image.MaxHeight) - { - throw new ArgumentOutOfRangeException( - $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " - + $"bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); - } - - Image image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); + var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); using (PixelAccessor pixels = image.Lock()) { switch (this.infoHeader.Compression) { case BmpCompression.RGB: - if (this.infoHeader.HeaderSize != 40) - { - throw new ImageFormatException($"Header Size value '{this.infoHeader.HeaderSize}' is not valid."); - } - if (this.infoHeader.BitsPerPixel == 32) { this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); @@ -157,6 +122,10 @@ namespace ImageSharp.Formats this.ReadRgbPalette(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted); } + break; + case BmpCompression.RLE8: + this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted); + break; default: throw new NotSupportedException("Does not support this kind of bitmap files."); @@ -171,6 +140,16 @@ namespace ImageSharp.Formats } } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public IImageInfo Identify(Stream stream) + { + this.ReadImageHeaders(stream, out _, out _); + return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData()); + } + /// /// Returns the y- value based on the given height. /// @@ -178,6 +157,7 @@ namespace ImageSharp.Formats /// The height of the bitmap. /// Whether the bitmap is inverted. /// The representing the inverted value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Invert(int y, int height, bool inverted) { int row; @@ -214,6 +194,127 @@ namespace ImageSharp.Formats return padding; } + /// + /// Performs final shifting from a 5bit value to an 8bit one. + /// + /// The masked and shifted value + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte GetBytesFrom5BitValue(int value) + { + return (byte)((value << 3) | (value >> 2)); + } + + /// + /// Looks up color values and builds the image from de-compressed RLE8 data. + /// Compresssed RLE8 stream is uncompressed by + /// + /// The pixel format. + /// The to assign the palette to. + /// The containing the colors. + /// The width of the bitmap. + /// The height of the bitmap. + /// Whether the bitmap is inverted. + private void ReadRle8(PixelAccessor pixels, byte[] colors, int width, int height, bool inverted) + where TPixel : struct, IPixel + { + var color = default(TPixel); + var rgba = new Rgba32(0, 0, 0, 255); + + using (var buffer = Buffer2D.CreateClean(width, height)) + { + this.UncompressRle8(width, buffer); + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span bufferRow = buffer.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(newY); + + for (int x = 0; x < width; x++) + { + rgba.Bgr = Unsafe.As(ref colors[bufferRow[x] * 4]); + color.PackFromRgba32(rgba); + pixelRow[x] = color; + } + } + } + } + + /// + /// Produce uncompressed bitmap data from RLE8 stream + /// + /// + /// RLE8 is a 2-byte run-length encoding + ///
If first byte is 0, the second byte may have special meaning + ///
Otherwise, first byte is the length of the run and second byte is the color for the run + ///
+ /// The width of the bitmap. + /// Buffer for uncompressed data. + private void UncompressRle8(int w, Span buffer) + { + byte[] cmd = new byte[2]; + int count = 0; + + while (count < buffer.Length) + { + if (this.currentStream.Read(cmd, 0, cmd.Length) != 2) + { + throw new Exception("Failed to read 2 bytes from stream"); + } + + if (cmd[0] == RleCommand) + { + switch (cmd[1]) + { + case RleEndOfBitmap: + return; + + case RleEndOfLine: + int extra = count % w; + if (extra > 0) + { + count += w - extra; + } + + break; + + case RleDelta: + int dx = this.currentStream.ReadByte(); + int dy = this.currentStream.ReadByte(); + count += (w * dy) + dx; + + break; + + default: + // If the second byte > 2, signals 'absolute mode' + // Take this number of bytes from the stream as uncompressed data + int length = cmd[1]; + int copyLength = length; + + // Absolute mode data is aligned to two-byte word-boundary + length += length & 1; + + byte[] run = new byte[length]; + this.currentStream.Read(run, 0, run.Length); + for (int i = 0; i < copyLength; i++) + { + buffer[count++] = run[i]; + } + + break; + } + } + else + { + for (int i = 0; i < cmd[0]; i++) + { + buffer[count++] = cmd[1]; + } + } + } + } + /// /// Reads the color palette from the stream. /// @@ -242,35 +343,36 @@ namespace ImageSharp.Formats padding = 4 - padding; } - byte[] row = new byte[arrayWidth + padding]; - TPixel color = default(TPixel); - - Rgba32 rgba = default(Rgba32); - - for (int y = 0; y < height; y++) + using (var row = Buffer.CreateClean(arrayWidth + padding)) { - int newY = Invert(y, height, inverted); - this.currentStream.Read(row, 0, row.Length); - int offset = 0; - Span pixelRow = pixels.GetRowSpan(y); + var color = default(TPixel); + var rgba = new Rgba32(0, 0, 0, 255); - // TODO: Could use PixelOperations here! - for (int x = 0; x < arrayWidth; x++) + for (int y = 0; y < height; y++) { - int colOffset = x * ppb; + int newY = Invert(y, height, inverted); + this.currentStream.Read(row.Array, 0, row.Length); + int offset = 0; + Span pixelRow = pixels.GetRowSpan(newY); - for (int shift = 0; shift < ppb && (x + shift) < width; shift++) + // TODO: Could use PixelOperations here! + for (int x = 0; x < arrayWidth; x++) { - int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4; - int newX = colOffset + shift; + int colOffset = x * ppb; - // Stored in b-> g-> r order. - rgba.Bgr = Unsafe.As(ref colors[colorIndex]); - color.PackFromRgba32(rgba); - pixelRow[newX] = color; - } + for (int shift = 0; shift < ppb && (x + shift) < width; shift++) + { + int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4; + int newX = colOffset + shift; - offset++; + // Stored in b-> g-> r order. + rgba.Bgr = Unsafe.As(ref colors[colorIndex]); + color.PackFromRgba32(rgba); + pixelRow[newX] = color; + } + + offset++; + } } } } @@ -286,36 +388,31 @@ namespace ImageSharp.Formats private void ReadRgb16(PixelAccessor pixels, int width, int height, bool inverted) where TPixel : struct, IPixel { - // We divide here as we will store the colors in our floating point format. - const int ScaleR = 8; // 256/32 - const int ScaleG = 4; // 256/64 - const int ComponentCount = 2; - - TPixel color = default(TPixel); - Rgba32 rgba = new Rgba32(0, 0, 0, 255); + int padding = CalculatePadding(width, 2); + int stride = (width * 2) + padding; + var color = default(TPixel); + var rgba = new Rgba32(0, 0, 0, 255); - using (PixelArea row = new PixelArea(width, ComponentOrder.Xyz)) + using (var buffer = new Buffer(stride)) { for (int y = 0; y < height; y++) { - row.Read(this.currentStream); - + this.currentStream.Read(buffer.Array, 0, stride); int newY = Invert(y, height, inverted); - Span pixelRow = pixels.GetRowSpan(newY); int offset = 0; for (int x = 0; x < width; x++) { - short temp = BitConverter.ToInt16(row.Bytes, offset); + short temp = BitConverter.ToInt16(buffer.Array, offset); - rgba.R = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); - rgba.G = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); - rgba.B = (byte)((temp & Rgb16BMask) * ScaleR); + rgba.R = GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10); + rgba.G = GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5); + rgba.B = GetBytesFrom5BitValue(temp & Rgb16BMask); color.PackFromRgba32(rgba); pixelRow[x] = color; - offset += ComponentCount; + offset += 2; } } } @@ -333,7 +430,7 @@ namespace ImageSharp.Formats where TPixel : struct, IPixel { int padding = CalculatePadding(width, 3); - using (PixelArea row = new PixelArea(width, ComponentOrder.Zyx, padding)) + using (var row = new PixelArea(width, ComponentOrder.Zyx, padding)) { for (int y = 0; y < height; y++) { @@ -357,7 +454,7 @@ namespace ImageSharp.Formats where TPixel : struct, IPixel { int padding = CalculatePadding(width, 4); - using (PixelArea row = new PixelArea(width, ComponentOrder.Zyxw, padding)) + using (var row = new PixelArea(width, ComponentOrder.Zyxw, padding)) { for (int y = 0; y < height; y++) { @@ -374,11 +471,85 @@ namespace ImageSharp.Formats ///
private void ReadInfoHeader() { - byte[] data = new byte[BmpInfoHeader.Size]; + byte[] data = new byte[BmpInfoHeader.MaxHeaderSize]; + + // read header size + this.currentStream.Read(data, 0, BmpInfoHeader.HeaderSizeSize); + int headerSize = BitConverter.ToInt32(data, 0); + if (headerSize < BmpInfoHeader.BitmapCoreHeaderSize) + { + throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported."); + } + + int skipAmmount = 0; + if (headerSize > BmpInfoHeader.MaxHeaderSize) + { + skipAmmount = headerSize - BmpInfoHeader.MaxHeaderSize; + headerSize = BmpInfoHeader.MaxHeaderSize; + } + + // read the rest of the header + this.currentStream.Read(data, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); + + switch (headerSize) + { + case BmpInfoHeader.BitmapCoreHeaderSize: + this.infoHeader = this.ParseBitmapCoreHeader(data); + break; + case BmpInfoHeader.BitmapInfoHeaderSize: + this.infoHeader = this.ParseBitmapInfoHeader(data); + break; + default: + if (headerSize > BmpInfoHeader.BitmapInfoHeaderSize) + { + this.infoHeader = this.ParseBitmapInfoHeader(data); + break; + } + else + { + throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported."); + } + } - this.currentStream.Read(data, 0, BmpInfoHeader.Size); + // skip the remaining header because we can't read those parts + this.currentStream.Skip(skipAmmount); + } - this.infoHeader = new BmpInfoHeader + /// + /// Parses the from the stream, assuming it uses the BITMAPCOREHEADER format. + /// + /// Header bytes read from the stream + /// Parsed header + /// + private BmpInfoHeader ParseBitmapCoreHeader(byte[] data) + { + return new BmpInfoHeader + { + HeaderSize = BitConverter.ToInt32(data, 0), + Width = BitConverter.ToUInt16(data, 4), + Height = BitConverter.ToUInt16(data, 6), + Planes = BitConverter.ToInt16(data, 8), + BitsPerPixel = BitConverter.ToInt16(data, 10), + + // the rest is not present in the core header + ImageSize = 0, + XPelsPerMeter = 0, + YPelsPerMeter = 0, + ClrUsed = 0, + ClrImportant = 0, + Compression = BmpCompression.RGB + }; + } + + /// + /// Parses the from the stream, assuming it uses the BITMAPINFOHEADER format. + /// + /// Header bytes read from the stream + /// Parsed header + /// + private BmpInfoHeader ParseBitmapInfoHeader(byte[] data) + { + return new BmpInfoHeader { HeaderSize = BitConverter.ToInt32(data, 0), Width = BitConverter.ToInt32(data, 4), @@ -411,5 +582,73 @@ namespace ImageSharp.Formats Offset = BitConverter.ToInt32(data, 10) }; } + + /// + /// Reads the and from the stream and sets the corresponding fields. + /// + private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette) + { + this.currentStream = stream; + + try + { + this.ReadFileHeader(); + this.ReadInfoHeader(); + + // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 + // If the height is negative, then this is a Windows bitmap whose origin + // is the upper-left corner and not the lower-left.The inverted flag + // indicates a lower-left origin.Our code will be outputting an + // upper-left origin pixel array. + inverted = false; + if (this.infoHeader.Height < 0) + { + inverted = true; + this.infoHeader.Height = -this.infoHeader.Height; + } + + int colorMapSize = -1; + + if (this.infoHeader.ClrUsed == 0) + { + if (this.infoHeader.BitsPerPixel == 1 || + this.infoHeader.BitsPerPixel == 4 || + this.infoHeader.BitsPerPixel == 8) + { + colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4; + } + } + else + { + colorMapSize = this.infoHeader.ClrUsed * 4; + } + + palette = null; + + if (colorMapSize > 0) + { + // 256 * 4 + if (colorMapSize > 1024) + { + throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); + } + + palette = new byte[colorMapSize]; + + this.currentStream.Read(palette, 0, colorMapSize); + } + + if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue) + { + throw new ArgumentOutOfRangeException( + $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " + + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'"); + } + } + catch (IndexOutOfRangeException e) + { + throw new ImageFormatException("Bitmap does not have a valid format.", e); + } + } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index dfba0b41c0..366afceb5f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index e41c295012..d34d170847 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - - using ImageSharp.PixelFormats; - - using IO; +using System; +using System.IO; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// @@ -37,12 +33,12 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. - public void Encode(ImageBase image, Stream stream) + public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); @@ -58,7 +54,7 @@ namespace ImageSharp.Formats BmpInfoHeader infoHeader = new BmpInfoHeader { - HeaderSize = BmpInfoHeader.Size, + HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize, Height = image.Height, Width = image.Width, BitsPerPixel = bpp, @@ -77,7 +73,7 @@ namespace ImageSharp.Formats WriteHeader(writer, fileHeader); this.WriteInfo(writer, infoHeader); - this.WriteImage(writer, image); + this.WriteImage(writer, image.Frames.RootFrame); writer.Flush(); } @@ -129,9 +125,9 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing the stream to write to. /// - /// The containing pixel data. + /// The containing pixel data. /// - private void WriteImage(EndianBinaryWriter writer, ImageBase image) + private void WriteImage(EndianBinaryWriter writer, ImageFrame image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) diff --git a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs index f9b20a48f8..4255ecae49 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Stores general information about the Bitmap file. diff --git a/src/ImageSharp/Formats/Bmp/BmpFormat.cs b/src/ImageSharp/Formats/Bmp/BmpFormat.cs index fb65f34d7d..64c6574c1e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFormat.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFormat.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// - /// Registers the image encoders, decoders and mime type detectors for the jpeg format. + /// Registers the image encoders, decoders and mime type detectors for the bmp format. /// internal sealed class BmpFormat : IImageFormat { diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index 697ee0f981..9c9786e0af 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Detects bmp file headers /// @@ -20,7 +18,7 @@ namespace ImageSharp.Formats { if (this.IsSupportedFileFormat(header)) { - return ImageFormats.Bitmap; + return ImageFormats.Bmp; } return null; diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index dc6a489d34..b24404cac0 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -1,8 +1,6 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// This block of bytes tells the application detailed information @@ -13,12 +11,27 @@ namespace ImageSharp.Formats internal sealed class BmpInfoHeader { /// - /// Defines of the data structure in the bitmap file. + /// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file. /// - public const int Size = 40; + public const int BitmapInfoHeaderSize = 40; /// - /// Gets or sets the size of this header (40 bytes) + /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file. + /// + public const int BitmapCoreHeaderSize = 12; + + /// + /// Defines the size of the biggest supported header data structure in the bitmap file. + /// + public const int MaxHeaderSize = BitmapInfoHeaderSize; + + /// + /// Defines the size of the field. + /// + public const int HeaderSizeSize = 4; + + /// + /// Gets or sets the size of this header /// public int HeaderSize { get; set; } diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs index 9285b9cf7a..920c9ce028 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image decoder options for decoding Windows bitmap streams. /// diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index dd17043fad..c4e219889d 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Configuration options for use during bmp encoding /// diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index aba24f9997..935ce8f4ad 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,11 +22,20 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsBmp(this Image source, Stream stream) + public static void SaveAsBmp(this Image source, Stream stream) + where TPixel : struct, IPixel + => source.SaveAsBmp(stream, null); + + /// + /// Saves the image to the given stream with the bmp format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. + public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, new BmpEncoder()); + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp)); } } diff --git a/src/ImageSharp/Formats/Gif/DisposalMethod.cs b/src/ImageSharp/Formats/Gif/DisposalMethod.cs index 80b5f3c6e7..f553c204b7 100644 --- a/src/ImageSharp/Formats/Gif/DisposalMethod.cs +++ b/src/ImageSharp/Formats/Gif/DisposalMethod.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// Provides enumeration for instructing the decoder what to do with the last image diff --git a/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs b/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs new file mode 100644 index 0000000000..05791c92e5 --- /dev/null +++ b/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Gif +{ + /// + /// Enumerated frame process modes to apply to multi-frame images. + /// + public enum FrameDecodingMode + { + /// + /// Decodes all the frames of a multi-frame image. + /// + All, + + /// + /// Decodes only the first frame of a multi-frame image. + /// + First + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs index ee134d66cd..4c42a833c0 100644 --- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs +++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// Registers the image encoders, decoders and mime type detectors for the gif format. diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs index 9bec6c48f9..d448cf7838 100644 --- a/src/ImageSharp/Formats/Gif/GifConstants.cs +++ b/src/ImageSharp/Formats/Gif/GifConstants.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.Text; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Constants that define specific points within a gif. /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 927289094f..c81c51e8b4 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -1,20 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Decoder for generating an image out of a gif encoded stream. /// - public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions + public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -26,12 +22,26 @@ namespace ImageSharp.Formats /// public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding; + /// + /// Gets or sets the decoding mode for multi-frame images + /// + public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All; + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { - var decoder = new GifDecoderCore(configuration, this); - return decoder.Decode(stream); + var decoder = new GifDecoderCore(configuration, this); + return decoder.Decode(stream); + } + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + var decoder = new GifDecoderCore(configuration, this); + return decoder.Identify(stream); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index bb230beac7..3c22518057 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -1,25 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Gif { - using System; - using System.Buffers; - using System.IO; - using System.Runtime.CompilerServices; - using System.Text; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - /// /// Performs the gif decoding operation. /// - /// The pixel format. - internal sealed class GifDecoderCore - where TPixel : struct, IPixel + internal sealed class GifDecoderCore { /// /// The temp buffer used to reduce allocations. @@ -39,18 +37,13 @@ namespace ImageSharp.Formats /// /// The global color table. /// - private byte[] globalColorTable; + private Buffer globalColorTable; /// /// The global color table length /// private int globalColorTableLength; - /// - /// The previous frame. - /// - private ImageFrame previousFrame; - /// /// The area to restore. /// @@ -72,12 +65,7 @@ namespace ImageSharp.Formats private ImageMetaData metaData; /// - /// The image to decode the information to. - /// - private Image image; - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The configuration. /// The decoder options. @@ -85,6 +73,7 @@ namespace ImageSharp.Formats { this.TextEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding; this.IgnoreMetadata = options.IgnoreMetadata; + this.DecodingMode = options.DecodingMode; this.configuration = configuration ?? Configuration.Default; } @@ -96,33 +85,94 @@ namespace ImageSharp.Formats /// /// Gets the text encoding /// - public Encoding TextEncoding { get; private set; } + public Encoding TextEncoding { get; } + + /// + /// Gets the decoding mode for multi-frame images + /// + public FrameDecodingMode DecodingMode { get; } /// /// Decodes the stream to the image. /// + /// The pixel format. /// The stream containing image data. /// The decoded image - public Image Decode(Stream stream) + public Image Decode(Stream stream) + where TPixel : struct, IPixel { + Image image = null; + ImageFrame previousFrame = null; try { - this.metaData = new ImageMetaData(); + this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); - this.currentStream = stream; + // Loop though the respective gif parts and read the data. + int nextFlag = stream.ReadByte(); + while (nextFlag != GifConstants.Terminator) + { + if (nextFlag == GifConstants.ImageLabel) + { + if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First) + { + break; + } - // Skip the identifier - this.currentStream.Skip(6); - this.ReadLogicalScreenDescriptor(); + this.ReadFrame(ref image, ref previousFrame); + } + else if (nextFlag == GifConstants.ExtensionIntroducer) + { + int label = stream.ReadByte(); + switch (label) + { + case GifConstants.GraphicControlLabel: + this.ReadGraphicalControlExtension(); + break; + case GifConstants.CommentLabel: + this.ReadComments(); + break; + case GifConstants.ApplicationExtensionLabel: - if (this.logicalScreenDescriptor.GlobalColorTableFlag) - { - this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.globalColorTable = ArrayPool.Shared.Rent(this.globalColorTableLength); + // The application extension length should be 11 but we've got test images that incorrectly + // set this to 252. + int appLength = stream.ReadByte(); + this.Skip(appLength); // No need to read. + break; + case GifConstants.PlainTextLabel: + int plainLength = stream.ReadByte(); + this.Skip(plainLength); // Not supported by any known decoder. + break; + } + } + else if (nextFlag == GifConstants.EndIntroducer) + { + break; + } - // Read the global color table from the stream - stream.Read(this.globalColorTable, 0, this.globalColorTableLength); + nextFlag = stream.ReadByte(); + if (nextFlag == -1) + { + break; + } } + } + finally + { + this.globalColorTable?.Dispose(); + } + + return image; + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public IImageInfo Identify(Stream stream) + { + try + { + this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream); // Loop though the respective gif parts and read the data. int nextFlag = stream.ReadByte(); @@ -130,7 +180,8 @@ namespace ImageSharp.Formats { if (nextFlag == GifConstants.ImageLabel) { - this.ReadFrame(); + // Skip image block + this.Skip(0); } else if (nextFlag == GifConstants.ExtensionIntroducer) { @@ -138,16 +189,23 @@ namespace ImageSharp.Formats switch (label) { case GifConstants.GraphicControlLabel: - this.ReadGraphicalControlExtension(); + + // Skip graphic control extension block + this.Skip(0); break; case GifConstants.CommentLabel: this.ReadComments(); break; case GifConstants.ApplicationExtensionLabel: - this.Skip(12); // No need to read. + + // The application extension length should be 11 but we've got test images that incorrectly + // set this to 252. + int appLength = stream.ReadByte(); + this.Skip(appLength); // No need to read. break; case GifConstants.PlainTextLabel: - this.Skip(13); // Not supported by any known decoder. + int plainLength = stream.ReadByte(); + this.Skip(plainLength); // Not supported by any known decoder. break; } } @@ -157,17 +215,18 @@ namespace ImageSharp.Formats } nextFlag = stream.ReadByte(); + if (nextFlag == -1) + { + break; + } } } finally { - if (this.globalColorTable != null) - { - ArrayPool.Shared.Return(this.globalColorTable); - } + this.globalColorTable?.Dispose(); } - return this.image; + return new ImageInfo(new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, this.metaData); } /// @@ -225,6 +284,7 @@ namespace ImageSharp.Formats { Width = BitConverter.ToInt16(this.buffer, 0), Height = BitConverter.ToInt16(this.buffer, 2), + BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1 BackgroundColorIndex = this.buffer[5], PixelAspectRatio = this.buffer[6], GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1, @@ -235,14 +295,6 @@ namespace ImageSharp.Formats { throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); } - - /* // No point doing this as the max width/height is always int.Max and that always bigger than the max size of a gif which is stored in a short. - if (this.logicalScreenDescriptor.Width > Image.MaxWidth || this.logicalScreenDescriptor.Height > Image.MaxHeight) - { - throw new ArgumentOutOfRangeException( - $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); - } - */ } /// @@ -299,39 +351,38 @@ namespace ImageSharp.Formats /// /// Reads an individual gif frame. /// - private void ReadFrame() + /// The pixel format. + /// The image to decode the information to. + /// The previous frame. + private void ReadFrame(ref Image image, ref ImageFrame previousFrame) + where TPixel : struct, IPixel { GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); - byte[] localColorTable = null; - byte[] indices = null; + Buffer localColorTable = null; + Buffer indices = null; try { // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. - int length = this.globalColorTableLength; if (imageDescriptor.LocalColorTableFlag) { - length = imageDescriptor.LocalColorTableSize * 3; - localColorTable = ArrayPool.Shared.Rent(length); - this.currentStream.Read(localColorTable, 0, length); + int length = imageDescriptor.LocalColorTableSize * 3; + localColorTable = Buffer.CreateClean(length); + this.currentStream.Read(localColorTable.Array, 0, length); } - indices = ArrayPool.Shared.Rent(imageDescriptor.Width * imageDescriptor.Height); + indices = Buffer.CreateClean(imageDescriptor.Width * imageDescriptor.Height); this.ReadFrameIndices(imageDescriptor, indices); - this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, length, imageDescriptor); + this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor); // Skip any remaining blocks this.Skip(0); } finally { - if (localColorTable != null) - { - ArrayPool.Shared.Return(localColorTable); - } - - ArrayPool.Shared.Return(indices); + localColorTable?.Dispose(); + indices?.Dispose(); } } @@ -341,7 +392,7 @@ namespace ImageSharp.Formats /// The . /// The pixel array to write to. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadFrameIndices(GifImageDescriptor imageDescriptor, byte[] indices) + private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span indices) { int dataSize = this.currentStream.ReadByte(); using (var lzwDecoder = new LzwDecoder(this.currentStream)) @@ -353,47 +404,48 @@ namespace ImageSharp.Formats /// /// Reads the frames colors, mapping indices to colors. /// + /// The pixel format. + /// The image to decode the information to. + /// The previous frame. /// The indexed pixels. /// The color table containing the available colors. - /// The color table length. /// The - private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, int colorTableLength, GifImageDescriptor descriptor) + private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Span indices, Span colorTable, GifImageDescriptor descriptor) + where TPixel : struct, IPixel { int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; - ImageFrame previousFrame = null; + ImageFrame prevFrame = null; ImageFrame currentFrame = null; - ImageBase image; + ImageFrame imageFrame; - if (this.previousFrame == null) + if (previousFrame == null) { // This initializes the image to become fully transparent because the alpha channel is zero. - this.image = new Image(this.configuration, imageWidth, imageHeight, this.metaData); + image = new Image(this.configuration, imageWidth, imageHeight, this.metaData); - this.SetFrameMetaData(this.metaData); + this.SetFrameMetaData(image.Frames.RootFrame.MetaData); - image = this.image; + imageFrame = image.Frames.RootFrame; } else { if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) { - previousFrame = this.previousFrame; + prevFrame = previousFrame; } - currentFrame = this.previousFrame.Clone(); + currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection this.SetFrameMetaData(currentFrame.MetaData); - image = currentFrame; - - this.RestoreToBackground(image); + imageFrame = currentFrame; - this.image.Frames.Add(currentFrame); + this.RestoreToBackground(imageFrame, image.Width, image.Height); } int i = 0; @@ -438,11 +490,12 @@ namespace ImageSharp.Formats writeY = y; } - Span rowSpan = image.GetRowSpan(writeY); + Span rowSpan = imageFrame.GetPixelRowSpan(writeY); - Rgba32 rgba = new Rgba32(0, 0, 0, 255); + var rgba = new Rgba32(0, 0, 0, 255); - for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) + // #403 The left + width value can be larger than the image width + for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < rowSpan.Length; x++) { int index = indices[i]; @@ -462,13 +515,13 @@ namespace ImageSharp.Formats } } - if (previousFrame != null) + if (prevFrame != null) { - this.previousFrame = previousFrame; + previousFrame = prevFrame; return; } - this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame; + previousFrame = currentFrame ?? image.Frames.RootFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) @@ -480,8 +533,12 @@ namespace ImageSharp.Formats /// /// Restores the current frame area to the background. /// + /// The pixel format. /// The frame. - private void RestoreToBackground(ImageBase frame) + /// Width of the image. + /// Height of the image. + private void RestoreToBackground(ImageFrame frame, int imageWidth, int imageHeight) + where TPixel : struct, IPixel { if (this.restoreArea == null) { @@ -489,8 +546,8 @@ namespace ImageSharp.Formats } // Optimization for when the size of the frame is the same as the image size. - if (this.restoreArea.Value.Width == this.image.Width && - this.restoreArea.Value.Height == this.image.Height) + if (this.restoreArea.Value.Width == imageWidth && + this.restoreArea.Value.Height == imageHeight) { using (PixelAccessor pixelAccessor = frame.Lock()) { @@ -517,18 +574,42 @@ namespace ImageSharp.Formats /// /// Sets the frames metadata. /// - /// The meta data. + /// The meta data. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetFrameMetaData(IMetaData metaData) + private void SetFrameMetaData(ImageFrameMetaData meta) { if (this.graphicsControlExtension != null) { if (this.graphicsControlExtension.DelayTime > 0) { - metaData.FrameDelay = this.graphicsControlExtension.DelayTime; + meta.FrameDelay = this.graphicsControlExtension.DelayTime; } - metaData.DisposalMethod = this.graphicsControlExtension.DisposalMethod; + meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod; + } + } + + /// + /// Reads the logical screen descriptor and global color table blocks + /// + /// The stream containing image data. + private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream) + { + this.metaData = new ImageMetaData(); + + this.currentStream = stream; + + // Skip the identifier + this.currentStream.Skip(6); + this.ReadLogicalScreenDescriptor(); + + if (this.logicalScreenDescriptor.GlobalColorTableFlag) + { + this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; + this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength); + + // Read the global color table from the stream + stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); } } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index b48db56356..ccf46a17d6 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Image encoder for writing image data to a stream in gif format. /// @@ -46,7 +44,7 @@ namespace ImageSharp.Formats public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - GifEncoderCore encoder = new GifEncoderCore(this); + var encoder = new GifEncoderCore(this); encoder.Encode(image, stream); } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 81b3cfba4a..41c8e944d8 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.IO; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; + +namespace SixLabors.ImageSharp.Formats.Gif { - using System; - using System.Buffers; - using System.IO; - using System.Linq; - using System.Text; - using ImageSharp.PixelFormats; - - using IO; - using Quantizers; - /// /// Performs the gif encoding operation. /// @@ -92,20 +90,18 @@ namespace ImageSharp.Formats var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); // Ensure that pallete size can be set but has a fallback. - int paletteSize = this.paletteSize; - paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256; + int size = this.paletteSize; + size = size > 0 ? size.Clamp(1, 256) : 256; // Get the number of bits. - this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize); + this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(size); - this.hasFrames = image.Frames.Any(); + this.hasFrames = image.Frames.Count > 1; - // Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames. - var ditheredQuantizer = (IQuantizer)this.quantizer; - ditheredQuantizer.Dither = !this.hasFrames; + var pixelQuantizer = (IQuantizer)this.quantizer; // Quantize the image returning a palette. - QuantizedImage quantized = ditheredQuantizer.Quantize(image, paletteSize); + QuantizedImage quantized = pixelQuantizer.Quantize(image.Frames.RootFrame, size); int index = this.GetTransparentIndex(quantized); @@ -116,28 +112,27 @@ namespace ImageSharp.Formats this.WriteLogicalScreenDescriptor(image, writer, index); // Write the first frame. - this.WriteGraphicalControlExtension(image.MetaData, writer, index); this.WriteComments(image, writer); - this.WriteImageDescriptor(image, writer); - this.WriteColorTable(quantized, writer); - this.WriteImageData(quantized, writer); // Write additional frames. if (this.hasFrames) { this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count); + } - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < image.Frames.Count; i++) + foreach (ImageFrame frame in image.Frames) + { + if (quantized == null) { - ImageFrame frame = image.Frames[i]; - QuantizedImage quantizedFrame = ditheredQuantizer.Quantize(frame, paletteSize); - - this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame)); - this.WriteImageDescriptor(frame, writer); - this.WriteColorTable(quantizedFrame, writer); - this.WriteImageData(quantizedFrame, writer); + quantized = pixelQuantizer.Quantize(frame, size); } + + this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantized)); + this.WriteImageDescriptor(frame, writer); + this.WriteColorTable(quantized, writer); + this.WriteImageData(quantized, writer); + + quantized = null; // so next frame can regenerate it } // TODO: Write extension etc @@ -159,18 +154,14 @@ namespace ImageSharp.Formats { // Transparent pixels are much more likely to be found at the end of a palette int index = -1; + var trans = default(Rgba32); for (int i = quantized.Palette.Length - 1; i >= 0; i--) { - quantized.Palette[i].ToXyzwBytes(this.buffer, 0); + quantized.Palette[i].ToRgba32(ref trans); - if (this.buffer[3] > 0) - { - continue; - } - else + if (trans.Equals(default(Rgba32))) { index = i; - break; } } @@ -255,7 +246,7 @@ namespace ImageSharp.Formats /// Writes the image comments to the stream. /// /// The pixel format. - /// The to be encoded. + /// The to be encoded. /// The stream to write to. private void WriteComments(Image image, EndianBinaryWriter writer) where TPixel : struct, IPixel @@ -290,7 +281,7 @@ namespace ImageSharp.Formats /// The metadata of the image or frame. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) + private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) { var extension = new GifGraphicsControlExtension { @@ -323,9 +314,9 @@ namespace ImageSharp.Formats /// Writes the image descriptor to the stream. /// /// The pixel format. - /// The to be encoded. + /// The to be encoded. /// The stream to write to. - private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) + private void WriteImageDescriptor(ImageFrame image, EndianBinaryWriter writer) where TPixel : struct, IPixel { writer.Write(GifConstants.ImageDescriptorLabel); // 2c @@ -349,7 +340,7 @@ namespace ImageSharp.Formats /// Writes the color table to the stream. /// /// The pixel format. - /// The to encode. + /// The to encode. /// The writer to write to the stream with. private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) where TPixel : struct, IPixel @@ -360,16 +351,16 @@ namespace ImageSharp.Formats // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); - + var rgb = default(Rgb24); try { for (int i = 0; i < pixelCount; i++) { int offset = i * 3; - image.Palette[i].ToXyzBytes(this.buffer, 0); - colorTable[offset] = this.buffer[0]; - colorTable[offset + 1] = this.buffer[1]; - colorTable[offset + 2] = this.buffer[2]; + image.Palette[i].ToRgb24(ref rgb); + colorTable[offset] = rgb.R; + colorTable[offset + 1] = rgb.G; + colorTable[offset + 2] = rgb.B; } writer.Write(colorTable, 0, colorTableLength); diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs index ea7b72d327..6353690f47 100644 --- a/src/ImageSharp/Formats/Gif/GifFormat.cs +++ b/src/ImageSharp/Formats/Gif/GifFormat.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// - /// Registers the image encoders, decoders and mime type detectors for the jpeg format. + /// Registers the image encoders, decoders and mime type detectors for the gif format. /// internal sealed class GifFormat : IImageFormat { diff --git a/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs b/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs index 04fcfc516c..36346f6062 100644 --- a/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Detects gif file headers /// diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs index caaa8932bb..a2288f30a4 100644 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs @@ -1,16 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System.Text; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Decoder for generating an image out of a gif encoded stream. /// @@ -25,5 +19,10 @@ namespace ImageSharp.Formats /// Gets the encoding that should be used when reading comments. /// Encoding TextEncoding { get; } + + /// + /// Gets the decoding mode for multi-frame images + /// + FrameDecodingMode DecodingMode { get; } } } diff --git a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs index c38ec7e451..374dea6595 100644 --- a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// The configuration options used for encoding gifs /// diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index ea9c9b5047..939eb456e1 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,14 +22,9 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsGif(this Image source, Stream stream) + public static void SaveAsGif(this Image source, Stream stream) where TPixel : struct, IPixel - { - return SaveAsGif(source, stream, null); - } + => source.SaveAsGif(stream, null); /// /// Saves the image to the given stream with the gif format. @@ -41,16 +34,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsGif(this Image source, Stream stream, GifEncoder encoder) + public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) where TPixel : struct, IPixel - { - encoder = encoder ?? new GifEncoder(); - encoder.Encode(source, stream); - - return source; - } + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif)); } } diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index bc0e9717c6..3284dad657 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Buffers; - using System.IO; +using System; +using System.Buffers; +using System.IO; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Decompresses and decodes data using the dynamic LZW algorithms. /// @@ -85,7 +83,7 @@ namespace ImageSharp.Formats /// The height of the pixel index array. /// Size of the data. /// The pixel array to decode to. - public void DecodePixels(int width, int height, int dataSize, byte[] pixels) + public void DecodePixels(int width, int height, int dataSize, Span pixels) { Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 69419444ff..b7bfd0fd25 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Buffers; - using System.IO; +using System; +using System.Buffers; +using System.IO; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. /// diff --git a/src/ImageSharp/Formats/Gif/PackedField.cs b/src/ImageSharp/Formats/Gif/PackedField.cs index 21d8f91f29..962e2082bf 100644 --- a/src/ImageSharp/Formats/Gif/PackedField.cs +++ b/src/ImageSharp/Formats/Gif/PackedField.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Represents a byte of data in a GIF data stream which contains a number /// of data items. diff --git a/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs index 503bd4fdf7..8cdd309d30 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// The Graphic Control Extension contains parameters used when diff --git a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs index 12c13beaf0..2ed9e47470 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// Each image in the Data Stream is composed of an Image Descriptor, diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs index fc2cc974aa..05f232a4be 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// The Logical Screen Descriptor contains the parameters @@ -24,6 +22,11 @@ namespace ImageSharp.Formats /// public short Height { get; set; } + /// + /// Gets or sets the color depth, in number of bits per pixel. + /// + public int BitsPerPixel { get; set; } + /// /// Gets or sets the index at the Global Color Table for the Background Color. /// The Background Color is the color used for those diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 66eabb1b82..ffc40314d8 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -1,23 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Encapsulates properties and methods required for decoding an image from a stream. /// public interface IImageDecoder { /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// /// The pixel format. /// The configuration for the image. diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index 4ad41ebc27..ac0b6e3119 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Encapsulates properties and methods required for encoding an image to a stream. /// diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs index d6ddc0b0bb..15bdc73a84 100644 --- a/src/ImageSharp/Formats/IImageFormat.cs +++ b/src/ImageSharp/Formats/IImageFormat.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats +{ /// /// Describes an image format. /// diff --git a/src/ImageSharp/Formats/IImageFormatDetector.cs b/src/ImageSharp/Formats/IImageFormatDetector.cs index a53da07e8b..8266439bdc 100644 --- a/src/ImageSharp/Formats/IImageFormatDetector.cs +++ b/src/ImageSharp/Formats/IImageFormatDetector.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.Text; +using System; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.Formats +{ /// /// Used for detecting mime types from a file header /// diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs new file mode 100644 index 0000000000..b7769e8955 --- /dev/null +++ b/src/ImageSharp/Formats/IImageInfoDetector.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Encapsulates methods used for detecting the raw image information without fully decoding it. + /// + 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); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf b/src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf new file mode 100644 index 0000000000..3423fb3159 Binary files /dev/null and b/src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf differ diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs new file mode 100644 index 0000000000..1066cfa808 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -0,0 +1,304 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + /// + /// Represents a Jpeg block with coefficiens. + /// + // ReSharper disable once InconsistentNaming + internal unsafe struct Block8x8 : IEquatable + { + /// + /// A number of scalar coefficients in a + /// + public const int Size = 64; + + /// + /// A fixed size buffer holding the values. + /// See: + /// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers + /// + /// + private fixed short data[Size]; + + /// + /// Initializes a new instance of the struct. + /// + /// A of coefficients + public Block8x8(Span coefficients) + { + ref byte selfRef = ref Unsafe.As(ref this); + ref byte sourceRef = ref coefficients.NonPortableCast().DangerousGetPinnableReference(); + Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short)); + } + + /// + /// Gets or sets a value at the given index + /// + /// The index + /// The value + public short this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + GuardBlockIndex(idx); + ref short selfRef = ref Unsafe.As(ref this); + return Unsafe.Add(ref selfRef, idx); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + GuardBlockIndex(idx); + ref short selfRef = ref Unsafe.As(ref this); + Unsafe.Add(ref selfRef, idx) = value; + } + } + + /// + /// Gets or sets a value in a row+coulumn of the 8x8 block + /// + /// The x position index in the row + /// The column index + /// The value + public short this[int x, int y] + { + get => this[(y * 8) + x]; + set => this[(y * 8) + x] = value; + } + + public static bool operator ==(Block8x8 left, Block8x8 right) + { + return left.Equals(right); + } + + public static bool operator !=(Block8x8 left, Block8x8 right) + { + return !left.Equals(right); + } + + /// + /// Multiply all elements by a given + /// + public static Block8x8 operator *(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val *= value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Divide all elements by a given + /// + public static Block8x8 operator /(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val /= value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Add an to all elements + /// + public static Block8x8 operator +(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val += value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Subtract an from all elements + /// + public static Block8x8 operator -(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val -= value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Pointer-based "Indexer" (getter part) + /// + /// Block pointer + /// Index + /// The scaleVec value at the specified index + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short GetScalarAt(Block8x8* blockPtr, int idx) + { + GuardBlockIndex(idx); + + short* fp = blockPtr->data; + return fp[idx]; + } + + /// + /// Pointer-based "Indexer" (setter part) + /// + /// Block pointer + /// Index + /// Value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetScalarAt(Block8x8* blockPtr, int idx, short value) + { + GuardBlockIndex(idx); + + short* fp = blockPtr->data; + fp[idx] = value; + } + + /// + /// Convert to + /// + public Block8x8F AsFloatBlock() + { + var result = default(Block8x8F); + result.LoadFrom(ref this); + return result; + } + + /// + /// Copy all elements to an array of . + /// + public short[] ToArray() + { + short[] result = new short[Size]; + this.CopyTo(result); + return result; + } + + /// + /// Copy elements into 'destination' Span of values + /// + public void CopyTo(Span destination) + { + ref byte selfRef = ref Unsafe.As(ref this); + ref byte destRef = ref destination.NonPortableCast().DangerousGetPinnableReference(); + Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short)); + } + + /// + /// Copy elements into 'destination' Span of values + /// + public void CopyTo(Span destination) + { + for (int i = 0; i < Size; i++) + { + destination[i] = this[i]; + } + } + + /// + /// Cast and copy -s from the beginning of 'source' span. + /// + public void LoadFrom(Span source) + { + for (int i = 0; i < Size; i++) + { + this[i] = (short)source[i]; + } + } + + [Conditional("DEBUG")] + private static void GuardBlockIndex(int idx) + { + DebugGuard.MustBeLessThan(idx, Size, nameof(idx)); + DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx)); + } + + /// + public override string ToString() + { + var bld = new StringBuilder(); + bld.Append('['); + for (int i = 0; i < Size; i++) + { + bld.Append(this[i]); + if (i < Size - 1) + { + bld.Append(','); + } + } + + bld.Append(']'); + return bld.ToString(); + } + + /// + public bool Equals(Block8x8 other) + { + for (int i = 0; i < Size; i++) + { + if (this[i] != other[i]) + { + return false; + } + } + + return true; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is Block8x8 && this.Equals((Block8x8)obj); + } + + /// + public override int GetHashCode() + { + return (this[0] * 31) + this[1]; + } + + /// + /// Calculate the total sum of absoulute differences of elements in 'a' and 'b'. + /// + public static long TotalDifference(ref Block8x8 a, ref Block8x8 b) + { + long result = 0; + for (int i = 0; i < Size; i++) + { + int d = a[i] - b[i]; + result += Math.Abs(d); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs new file mode 100644 index 0000000000..39a6bee2e4 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs @@ -0,0 +1,149 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + internal partial struct Block8x8F + { + /// + /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical. + /// + public void CopyTo(BufferArea area, int horizontalScale, int verticalScale) + { + if (horizontalScale == 1 && verticalScale == 1) + { + this.CopyTo(area); + return; + } + else if (horizontalScale == 2 && verticalScale == 2) + { + this.CopyTo2x2(area); + return; + } + + // TODO: Optimize: implement all the cases with loopless special code! (T4?) + for (int y = 0; y < 8; y++) + { + int yy = y * verticalScale; + int y8 = y * 8; + + for (int x = 0; x < 8; x++) + { + int xx = x * horizontalScale; + + float value = this[y8 + x]; + + for (int i = 0; i < verticalScale; i++) + { + for (int j = 0; j < horizontalScale; j++) + { + area[xx + j, yy + i] = value; + } + } + } + } + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(BufferArea area) + { + ref byte selfBase = ref Unsafe.As(ref this); + ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigo()); + int destStride = area.Stride * sizeof(float); + + CopyRowImpl(ref selfBase, ref destBase, destStride, 0); + CopyRowImpl(ref selfBase, ref destBase, destStride, 1); + CopyRowImpl(ref selfBase, ref destBase, destStride, 2); + CopyRowImpl(ref selfBase, ref destBase, destStride, 3); + CopyRowImpl(ref selfBase, ref destBase, destStride, 4); + CopyRowImpl(ref selfBase, ref destBase, destStride, 5); + CopyRowImpl(ref selfBase, ref destBase, destStride, 6); + CopyRowImpl(ref selfBase, ref destBase, destStride, 7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) + { + ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); + ref byte d = ref Unsafe.Add(ref destBase, row * destStride); + Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); + } + + private void CopyTo2x2(BufferArea area) + { + ref float destBase = ref area.GetReferenceToOrigo(); + int destStride = area.Stride; + + this.WidenCopyImpl2x2(ref destBase, 0, destStride); + this.WidenCopyImpl2x2(ref destBase, 1, destStride); + this.WidenCopyImpl2x2(ref destBase, 2, destStride); + this.WidenCopyImpl2x2(ref destBase, 3, destStride); + this.WidenCopyImpl2x2(ref destBase, 4, destStride); + this.WidenCopyImpl2x2(ref destBase, 5, destStride); + this.WidenCopyImpl2x2(ref destBase, 6, destStride); + this.WidenCopyImpl2x2(ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WidenCopyImpl2x2(ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; + Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl(ref Vector4 s, ref float destBase) + { + Unsafe.Add(ref destBase, 0) = s.X; + Unsafe.Add(ref destBase, 1) = s.X; + Unsafe.Add(ref destBase, 2) = s.Y; + Unsafe.Add(ref destBase, 3) = s.Y; + Unsafe.Add(ref destBase, 4) = s.Z; + Unsafe.Add(ref destBase, 5) = s.Z; + Unsafe.Add(ref destBase, 6) = s.W; + Unsafe.Add(ref destBase, 7) = s.W; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs new file mode 100644 index 0000000000..93e9e03885 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs @@ -0,0 +1,234 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +// +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + internal partial struct Block8x8F + { + private static readonly Vector4 CMin4 = new Vector4(0F); + private static readonly Vector4 CMax4 = new Vector4(255F); + private static readonly Vector4 COff4 = new Vector4(128F); + + /// + /// Transpose the block into the destination block. + /// + /// The destination block + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TransposeInto(ref Block8x8F d) + { + d.V0L.X = V0L.X; + d.V1L.X = V0L.Y; + d.V2L.X = V0L.Z; + d.V3L.X = V0L.W; + d.V4L.X = V0R.X; + d.V5L.X = V0R.Y; + d.V6L.X = V0R.Z; + d.V7L.X = V0R.W; + + d.V0L.Y = V1L.X; + d.V1L.Y = V1L.Y; + d.V2L.Y = V1L.Z; + d.V3L.Y = V1L.W; + d.V4L.Y = V1R.X; + d.V5L.Y = V1R.Y; + d.V6L.Y = V1R.Z; + d.V7L.Y = V1R.W; + + d.V0L.Z = V2L.X; + d.V1L.Z = V2L.Y; + d.V2L.Z = V2L.Z; + d.V3L.Z = V2L.W; + d.V4L.Z = V2R.X; + d.V5L.Z = V2R.Y; + d.V6L.Z = V2R.Z; + d.V7L.Z = V2R.W; + + d.V0L.W = V3L.X; + d.V1L.W = V3L.Y; + d.V2L.W = V3L.Z; + d.V3L.W = V3L.W; + d.V4L.W = V3R.X; + d.V5L.W = V3R.Y; + d.V6L.W = V3R.Z; + d.V7L.W = V3R.W; + + d.V0R.X = V4L.X; + d.V1R.X = V4L.Y; + d.V2R.X = V4L.Z; + d.V3R.X = V4L.W; + d.V4R.X = V4R.X; + d.V5R.X = V4R.Y; + d.V6R.X = V4R.Z; + d.V7R.X = V4R.W; + + d.V0R.Y = V5L.X; + d.V1R.Y = V5L.Y; + d.V2R.Y = V5L.Z; + d.V3R.Y = V5L.W; + d.V4R.Y = V5R.X; + d.V5R.Y = V5R.Y; + d.V6R.Y = V5R.Z; + d.V7R.Y = V5R.W; + + d.V0R.Z = V6L.X; + d.V1R.Z = V6L.Y; + d.V2R.Z = V6L.Z; + d.V3R.Z = V6L.W; + d.V4R.Z = V6R.X; + d.V5R.Z = V6R.Y; + d.V6R.Z = V6R.Z; + d.V7R.Z = V6R.W; + + d.V0R.W = V7L.X; + d.V1R.W = V7L.Y; + d.V2R.W = V7L.Z; + d.V3R.W = V7L.W; + d.V4R.W = V7R.X; + d.V5R.W = V7R.Y; + d.V6R.W = V7R.Z; + d.V7R.W = V7R.W; + } + + /// + /// Level shift by +128, clip to [0, 255] + /// + public void NormalizeColorsInplace() + { + this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4); + this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4); + this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4); + this.V1R = Vector4.Clamp(this.V1R + COff4, CMin4, CMax4); + this.V2L = Vector4.Clamp(this.V2L + COff4, CMin4, CMax4); + this.V2R = Vector4.Clamp(this.V2R + COff4, CMin4, CMax4); + this.V3L = Vector4.Clamp(this.V3L + COff4, CMin4, CMax4); + this.V3R = Vector4.Clamp(this.V3R + COff4, CMin4, CMax4); + this.V4L = Vector4.Clamp(this.V4L + COff4, CMin4, CMax4); + this.V4R = Vector4.Clamp(this.V4R + COff4, CMin4, CMax4); + this.V5L = Vector4.Clamp(this.V5L + COff4, CMin4, CMax4); + this.V5R = Vector4.Clamp(this.V5R + COff4, CMin4, CMax4); + this.V6L = Vector4.Clamp(this.V6L + COff4, CMin4, CMax4); + this.V6R = Vector4.Clamp(this.V6R + COff4, CMin4, CMax4); + this.V7L = Vector4.Clamp(this.V7L + COff4, CMin4, CMax4); + this.V7R = Vector4.Clamp(this.V7R + COff4, CMin4, CMax4); + } + + /// + /// AVX2-only variant for executing and in one step. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void NormalizeColorsAndRoundInplaceAvx2() + { + Vector off = new Vector(128f); + Vector max = new Vector(255F); + + ref Vector row0 = ref Unsafe.As>(ref this.V0L); + row0 = NormalizeAndRound(row0, off, max); + + ref Vector row1 = ref Unsafe.As>(ref this.V1L); + row1 = NormalizeAndRound(row1, off, max); + + ref Vector row2 = ref Unsafe.As>(ref this.V2L); + row2 = NormalizeAndRound(row2, off, max); + + ref Vector row3 = ref Unsafe.As>(ref this.V3L); + row3 = NormalizeAndRound(row3, off, max); + + ref Vector row4 = ref Unsafe.As>(ref this.V4L); + row4 = NormalizeAndRound(row4, off, max); + + ref Vector row5 = ref Unsafe.As>(ref this.V5L); + row5 = NormalizeAndRound(row5, off, max); + + ref Vector row6 = ref Unsafe.As>(ref this.V6L); + row6 = NormalizeAndRound(row6, off, max); + + ref Vector row7 = ref Unsafe.As>(ref this.V7L); + row7 = NormalizeAndRound(row7, off, max); + + } + + /// + /// Fill the block from 'source' doing short -> float conversion. + /// + public void LoadFrom(ref Block8x8 source) + { + ref short selfRef = ref Unsafe.As(ref source); + + this.V0L.X = Unsafe.Add(ref selfRef, 0); + this.V0L.Y = Unsafe.Add(ref selfRef, 1); + this.V0L.Z = Unsafe.Add(ref selfRef, 2); + this.V0L.W = Unsafe.Add(ref selfRef, 3); + this.V0R.X = Unsafe.Add(ref selfRef, 4); + this.V0R.Y = Unsafe.Add(ref selfRef, 5); + this.V0R.Z = Unsafe.Add(ref selfRef, 6); + this.V0R.W = Unsafe.Add(ref selfRef, 7); + + this.V1L.X = Unsafe.Add(ref selfRef, 8); + this.V1L.Y = Unsafe.Add(ref selfRef, 9); + this.V1L.Z = Unsafe.Add(ref selfRef, 10); + this.V1L.W = Unsafe.Add(ref selfRef, 11); + this.V1R.X = Unsafe.Add(ref selfRef, 12); + this.V1R.Y = Unsafe.Add(ref selfRef, 13); + this.V1R.Z = Unsafe.Add(ref selfRef, 14); + this.V1R.W = Unsafe.Add(ref selfRef, 15); + + this.V2L.X = Unsafe.Add(ref selfRef, 16); + this.V2L.Y = Unsafe.Add(ref selfRef, 17); + this.V2L.Z = Unsafe.Add(ref selfRef, 18); + this.V2L.W = Unsafe.Add(ref selfRef, 19); + this.V2R.X = Unsafe.Add(ref selfRef, 20); + this.V2R.Y = Unsafe.Add(ref selfRef, 21); + this.V2R.Z = Unsafe.Add(ref selfRef, 22); + this.V2R.W = Unsafe.Add(ref selfRef, 23); + + this.V3L.X = Unsafe.Add(ref selfRef, 24); + this.V3L.Y = Unsafe.Add(ref selfRef, 25); + this.V3L.Z = Unsafe.Add(ref selfRef, 26); + this.V3L.W = Unsafe.Add(ref selfRef, 27); + this.V3R.X = Unsafe.Add(ref selfRef, 28); + this.V3R.Y = Unsafe.Add(ref selfRef, 29); + this.V3R.Z = Unsafe.Add(ref selfRef, 30); + this.V3R.W = Unsafe.Add(ref selfRef, 31); + + this.V4L.X = Unsafe.Add(ref selfRef, 32); + this.V4L.Y = Unsafe.Add(ref selfRef, 33); + this.V4L.Z = Unsafe.Add(ref selfRef, 34); + this.V4L.W = Unsafe.Add(ref selfRef, 35); + this.V4R.X = Unsafe.Add(ref selfRef, 36); + this.V4R.Y = Unsafe.Add(ref selfRef, 37); + this.V4R.Z = Unsafe.Add(ref selfRef, 38); + this.V4R.W = Unsafe.Add(ref selfRef, 39); + + this.V5L.X = Unsafe.Add(ref selfRef, 40); + this.V5L.Y = Unsafe.Add(ref selfRef, 41); + this.V5L.Z = Unsafe.Add(ref selfRef, 42); + this.V5L.W = Unsafe.Add(ref selfRef, 43); + this.V5R.X = Unsafe.Add(ref selfRef, 44); + this.V5R.Y = Unsafe.Add(ref selfRef, 45); + this.V5R.Z = Unsafe.Add(ref selfRef, 46); + this.V5R.W = Unsafe.Add(ref selfRef, 47); + + this.V6L.X = Unsafe.Add(ref selfRef, 48); + this.V6L.Y = Unsafe.Add(ref selfRef, 49); + this.V6L.Z = Unsafe.Add(ref selfRef, 50); + this.V6L.W = Unsafe.Add(ref selfRef, 51); + this.V6R.X = Unsafe.Add(ref selfRef, 52); + this.V6R.Y = Unsafe.Add(ref selfRef, 53); + this.V6R.Z = Unsafe.Add(ref selfRef, 54); + this.V6R.W = Unsafe.Add(ref selfRef, 55); + + this.V7L.X = Unsafe.Add(ref selfRef, 56); + this.V7L.Y = Unsafe.Add(ref selfRef, 57); + this.V7L.Z = Unsafe.Add(ref selfRef, 58); + this.V7L.W = Unsafe.Add(ref selfRef, 59); + this.V7R.X = Unsafe.Add(ref selfRef, 60); + this.V7R.Y = Unsafe.Add(ref selfRef, 61); + this.V7R.Z = Unsafe.Add(ref selfRef, 62); + this.V7R.W = Unsafe.Add(ref selfRef, 63); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt new file mode 100644 index 0000000000..dc0996b65d --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt @@ -0,0 +1,136 @@ +<# +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +// +<# +char[] coordz = {'X', 'Y', 'Z', 'W'}; +#> +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + internal partial struct Block8x8F + { + private static readonly Vector4 CMin4 = new Vector4(0F); + private static readonly Vector4 CMax4 = new Vector4(255F); + private static readonly Vector4 COff4 = new Vector4(128F); + + /// + /// Transpose the block into the destination block. + /// + /// The destination block + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TransposeInto(ref Block8x8F d) + { + <# + PushIndent(" "); + + for (int i = 0; i < 8; i++) + { + char destCoord = coordz[i % 4]; + char destSide = (i / 4) % 2 == 0 ? 'L' : 'R'; + + for (int j = 0; j < 8; j++) + { + if(i > 0 && j == 0){ + WriteLine(""); + } + + char srcCoord = coordz[j % 4]; + char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; + + string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n"; + Write(expression); + } + } + PopIndent(); + #> + } + + /// + /// Level shift by +128, clip to [0, 255] + /// + public void NormalizeColorsInplace() + { + <# + + PushIndent(" "); + + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 2; j++) + { + char side = j == 0 ? 'L' : 'R'; + Write($"this.V{i}{side} = Vector4.Clamp(this.V{i}{side} + COff4, CMin4, CMax4);\r\n"); + } + } + PopIndent(); + #> + } + + /// + /// AVX2-only variant for executing and in one step. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void NormalizeColorsAndRoundInplaceAvx2() + { + Vector off = new Vector(128f); + Vector max = new Vector(255F); + <# + + for (int i = 0; i < 8; i++) + { + #> + + ref Vector row<#=i#> = ref Unsafe.As>(ref this.V<#=i#>L); + row<#=i#> = NormalizeAndRound(row<#=i#>, off, max); + <# + } + #> + + } + + /// + /// Fill the block from 'source' doing short -> float conversion. + /// + public void LoadFrom(ref Block8x8 source) + { + ref short selfRef = ref Unsafe.As(ref source); + + <# + PushIndent(" "); + for (int j = 0; j < 8; j++) + { + for (int i = 0; i < 8; i++) + { + char destCoord = coordz[i % 4]; + char destSide = (i / 4) % 2 == 0 ? 'L' : 'R'; + + if(j > 0 && i == 0){ + WriteLine(""); + } + + char srcCoord = coordz[j % 4]; + char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; + + string expression = $"this.V{j}{destSide}.{destCoord} = Unsafe.Add(ref selfRef, {j*8+i});\r\n"; + Write(expression); + + } + } + PopIndent(); + #> + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs similarity index 54% rename from src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs rename to src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs index 130b5856c0..2dd42288cb 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs @@ -1,35 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + // ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpg +namespace SixLabors.ImageSharp.Formats.Jpeg.Common { - using System; - using System.Buffers; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - /// - /// DCT code Ported from https://github.com/norishigefukushima/dct_simd + /// Represents a Jpeg block with coefficients. /// internal partial struct Block8x8F { - // Most of the static methods of this struct are instance methods by actual semantics: they use Block8x8F* as their first parameter. - // Example: GetScalarAt() and SetScalarAt() are really just other (optimized) versions of the indexer. - // It's much cleaner, easier and safer to work with the code, if the methods with same semantics are next to each other. -#pragma warning disable SA1204 // StaticElementsMustAppearBeforeInstanceElements - /// - /// Vector count + /// A number of scalar coefficients in a /// - public const int VectorCount = 16; - - /// - /// Scalar count - /// - public const int ScalarCount = VectorCount * 4; + public const int Size = 64; #pragma warning disable SA1600 // ElementsMustBeDocumented public Vector4 V0L; @@ -65,53 +55,95 @@ namespace ImageSharp.Formats.Jpg /// /// The index /// The float value at the specified index - public unsafe float this[int idx] + public float this[int idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - fixed (Block8x8F* p = &this) - { - float* fp = (float*)p; - return fp[idx]; - } + GuardBlockIndex(idx); + ref float selfRef = ref Unsafe.As(ref this); + return Unsafe.Add(ref selfRef, idx); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - fixed (Block8x8F* p = &this) - { - float* fp = (float*)p; - fp[idx] = value; - } + GuardBlockIndex(idx); + ref float selfRef = ref Unsafe.As(ref this); + Unsafe.Add(ref selfRef, idx) = value; } } - /// - /// Pointer-based "Indexer" (getter part) - /// - /// Block pointer - /// Index - /// The scaleVec value at the specified index - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) + public float this[int x, int y] { - float* fp = (float*)blockPtr; - return fp[idx]; + get => this[(y * 8) + x]; + set => this[(y * 8) + x] = value; } - /// - /// Pointer-based "Indexer" (setter part) - /// - /// Block pointer - /// Index - /// Value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) + public static Block8x8F operator *(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val *= value; + result[i] = val; + } + + return result; + } + + public static Block8x8F operator /(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val /= value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F operator +(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val += value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F operator -(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val -= value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F Load(Span data) + { + var result = default(Block8x8F); + result.LoadFrom(data); + return result; + } + + public static Block8x8F Load(Span data) { - float* fp = (float*)blockPtr; - fp[idx] = value; + var result = default(Block8x8F); + result.LoadFrom(data); + return result; } /// @@ -129,12 +161,12 @@ namespace ImageSharp.Formats.Jpg /// /// Source [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void LoadFrom(MutableSpan source) + public void LoadFrom(Span source) { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount); - } + ref byte s = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte d = ref Unsafe.As(ref this); + + Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); } /// @@ -143,21 +175,21 @@ namespace ImageSharp.Formats.Jpg /// Block pointer /// Source [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan source) + public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source) { - Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount); + blockPtr->LoadFrom(source); } /// /// Load raw 32bit floating point data from source /// /// Source - public unsafe void LoadFrom(MutableSpan source) + public unsafe void LoadFrom(Span source) { fixed (Vector4* ptr = &this.V0L) { float* fp = (float*)ptr; - for (int i = 0; i < ScalarCount; i++) + for (int i = 0; i < Size; i++) { fp[i] = source[i]; } @@ -169,12 +201,12 @@ namespace ImageSharp.Formats.Jpg /// /// Destination [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(MutableSpan dest) + public void CopyTo(Span dest) { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount); - } + ref byte d = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref byte s = ref Unsafe.As(ref this); + + Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); } /// @@ -183,10 +215,10 @@ namespace ImageSharp.Formats.Jpg /// Pointer to block /// Destination [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan dest) + public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) { float* fPtr = (float*)blockPtr; - for (int i = 0; i < ScalarCount; i++) + for (int i = 0; i < Size; i++) { dest[i] = (byte)*fPtr; fPtr++; @@ -199,9 +231,9 @@ namespace ImageSharp.Formats.Jpg /// Block pointer /// Destination [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan dest) + public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) { - Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount); + blockPtr->CopyTo(dest); } /// @@ -213,7 +245,7 @@ namespace ImageSharp.Formats.Jpg { fixed (void* ptr = &this.V0L) { - Marshal.Copy((IntPtr)ptr, dest, 0, ScalarCount); + Marshal.Copy((IntPtr)ptr, dest, 0, Size); } } @@ -221,41 +253,72 @@ namespace ImageSharp.Formats.Jpg /// Copy raw 32bit floating point data to dest /// /// Destination - public unsafe void CopyTo(MutableSpan dest) + public unsafe void CopyTo(Span dest) { fixed (Vector4* ptr = &this.V0L) { float* fp = (float*)ptr; - for (int i = 0; i < ScalarCount; i++) + for (int i = 0; i < Size; i++) { dest[i] = (int)fp[i]; } } } + public float[] ToArray() + { + float[] result = new float[Size]; + this.CopyTo(result); + return result; + } + /// /// Multiply all elements of the block. /// - /// Vector to multiply by + /// The value to multiply by [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void MultiplyAllInplace(float scaleVec) - { - this.V0L *= scaleVec; - this.V0R *= scaleVec; - this.V1L *= scaleVec; - this.V1R *= scaleVec; - this.V2L *= scaleVec; - this.V2R *= scaleVec; - this.V3L *= scaleVec; - this.V3R *= scaleVec; - this.V4L *= scaleVec; - this.V4R *= scaleVec; - this.V5L *= scaleVec; - this.V5R *= scaleVec; - this.V6L *= scaleVec; - this.V6R *= scaleVec; - this.V7L *= scaleVec; - this.V7R *= scaleVec; + public void MultiplyInplace(float value) + { + this.V0L *= value; + this.V0R *= value; + this.V1L *= value; + this.V1R *= value; + this.V2L *= value; + this.V2R *= value; + this.V3L *= value; + this.V3R *= value; + this.V4L *= value; + this.V4R *= value; + this.V5L *= value; + this.V5R *= value; + this.V6L *= value; + this.V6R *= value; + this.V7L *= value; + this.V7R *= value; + } + + /// + /// Multiply all elements of the block by the corresponding elements of 'other' + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MultiplyInplace(ref Block8x8F other) + { + this.V0L *= other.V0L; + this.V0R *= other.V0R; + this.V1L *= other.V1L; + this.V1R *= other.V1R; + this.V2L *= other.V2L; + this.V2R *= other.V2R; + this.V3L *= other.V3L; + this.V3R *= other.V3R; + this.V4L *= other.V4L; + this.V4R *= other.V4R; + this.V5L *= other.V5L; + this.V5R *= other.V5R; + this.V6L *= other.V6L; + this.V6R *= other.V6R; + this.V7L *= other.V7L; + this.V7R *= other.V7R; } /// @@ -284,61 +347,37 @@ namespace ImageSharp.Formats.Jpg } /// - /// Un-zig + /// Quantize the block. /// /// Block pointer /// Qt pointer /// Unzig pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void UnZig(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) { float* b = (float*)blockPtr; float* qtp = (float*)qtPtr; - for (int zig = 0; zig < ScalarCount; zig++) + for (int qtIndex = 0; qtIndex < Size; qtIndex++) { - float* unzigPos = b + unzigPtr[zig]; + int blockIndex = unzigPtr[qtIndex]; + float* unzigPos = b + blockIndex; + float val = *unzigPos; - val *= qtp[zig]; + val *= qtp[qtIndex]; *unzigPos = val; } } /// - /// Level shift by +128, clip to [0, 255], and write to buffer. - /// - /// Color buffer - /// Stride offset - /// Temp Block pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyColorsTo(MutableSpan buffer, int stride, Block8x8F* tempBlockPtr) - { - this.TransformByteConvetibleColorValuesInto(ref *tempBlockPtr); - - float* src = (float*)tempBlockPtr; - for (int i = 0; i < 8; i++) - { - buffer[0] = (byte)src[0]; - buffer[1] = (byte)src[1]; - buffer[2] = (byte)src[2]; - buffer[3] = (byte)src[3]; - buffer[4] = (byte)src[4]; - buffer[5] = (byte)src[5]; - buffer[6] = (byte)src[6]; - buffer[7] = (byte)src[7]; - buffer.AddOffset(stride); - src += 8; - } - } - - /// + /// Quantize 'block' into 'dest' using the 'qt' quantization table: /// Unzig the elements of block into dest, while dividing them by elements of qt and "pre-rounding" the values. /// To finish the rounding it's enough to (int)-cast these values. /// /// Source block /// Destination block /// The quantization table - /// Pointer to elements of - public static unsafe void UnzigDivRound( + /// Pointer to elements of + public static unsafe void Quantize( Block8x8F* block, Block8x8F* dest, Block8x8F* qt, @@ -347,7 +386,7 @@ namespace ImageSharp.Formats.Jpg float* s = (float*)block; float* d = (float*)dest; - for (int zig = 0; zig < ScalarCount; zig++) + for (int zig = 0; zig < Size; zig++) { d[zig] = s[unzigPtr[zig]]; } @@ -402,6 +441,85 @@ namespace ImageSharp.Formats.Jpg a.V7R = DivideRound(a.V7R, b.V7R); } + public void RoundInto(ref Block8x8 dest) + { + for (int i = 0; i < Size; i++) + { + float val = this[i]; + if (val < 0) + { + val -= 0.5f; + } + else + { + val += 0.5f; + } + + dest[i] = (short)val; + } + } + + public Block8x8 RoundAsInt16Block() + { + var result = default(Block8x8); + this.RoundInto(ref result); + return result; + } + + /// + /// Level shift by +128, clip to [0..255], and round all the values in the block. + /// + public void NormalizeColorsAndRoundInplace() + { + if (SimdUtils.IsAvx2CompatibleArchitecture) + { + this.NormalizeColorsAndRoundInplaceAvx2(); + } + else + { + this.NormalizeColorsInplace(); + this.RoundInplace(); + } + } + + /// + /// Rounds all values in the block. + /// + public void RoundInplace() + { + for (int i = 0; i < Size; i++) + { + this[i] = MathF.Round(this[i]); + } + } + + /// + public override string ToString() + { + var bld = new StringBuilder(); + bld.Append('['); + for (int i = 0; i < Size; i++) + { + bld.Append(this[i]); + if (i < Size - 1) + { + bld.Append(','); + } + } + + bld.Append(']'); + return bld.ToString(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector NormalizeAndRound(Vector row, Vector off, Vector max) + { + row += off; + row = Vector.Max(row, Vector.Zero); + row = Vector.Min(row, max); + return row.FastRound(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) { @@ -411,5 +529,12 @@ namespace ImageSharp.Formats.Jpg // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend) return (dividend / divisor) + (sign * Offset); } + + [Conditional("DEBUG")] + private static void GuardBlockIndex(int idx) + { + DebugGuard.MustBeLessThan(idx, Size, nameof(idx)); + DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx)); + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs new file mode 100644 index 0000000000..0ec2297d76 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs @@ -0,0 +1,117 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Provides information about the Adobe marker segment. + /// + /// See the included 5116.DCT.pdf file in the source for more information. + internal struct AdobeMarker : IEquatable + { + /// + /// Gets the length of an adobe marker segment. + /// + public const int Length = 12; + + /// + /// Initializes a new instance of the struct. + /// + /// The DCT encode version + /// The horizontal downsampling hint used for DCT encoding + /// The vertical downsampling hint used for DCT encoding + /// The color transform model used + private AdobeMarker(short dctEncodeVersion, short app14Flags0, short app14Flags1, byte colorTransform) + { + this.DCTEncodeVersion = dctEncodeVersion; + this.APP14Flags0 = app14Flags0; + this.APP14Flags1 = app14Flags1; + this.ColorTransform = colorTransform; + } + + /// + /// Gets the DCT Encode Version + /// + public short DCTEncodeVersion { get; } + + /// + /// Gets the horizontal downsampling hint used for DCT encoding + /// 0x0 : (none - Chop) + /// Bit 15 : Encoded with Blend=1 downsampling + /// + public short APP14Flags0 { get; } + + /// + /// Gets the vertical downsampling hint used for DCT encoding + /// 0x0 : (none - Chop) + /// Bit 15 : Encoded with Blend=1 downsampling + /// + public short APP14Flags1 { get; } + + /// + /// Gets the colorspace transform model used + /// 00 : Unknown (RGB or CMYK) + /// 01 : YCbCr + /// 02 : YCCK + /// + public byte ColorTransform { get; } + + /// + /// Converts the specified byte array representation of an Adobe marker to its equivalent and + /// returns a value that indicates whether the conversion succeeded. + /// + /// The byte array containing metadata to parse + /// The marker to return. + public static bool TryParse(byte[] bytes, out AdobeMarker marker) + { + if (ProfileResolver.IsProfile(bytes, ProfileResolver.AdobeMarker)) + { + short dctEncodeVersion = (short)((bytes[5] << 8) | bytes[6]); + short app14Flags0 = (short)((bytes[7] << 8) | bytes[8]); + short app14Flags1 = (short)((bytes[9] << 8) | bytes[10]); + byte colorTransform = bytes[11]; + + marker = new AdobeMarker(dctEncodeVersion, app14Flags0, app14Flags1, colorTransform); + return true; + } + + marker = default(AdobeMarker); + return false; + } + + /// + public bool Equals(AdobeMarker other) + { + return this.DCTEncodeVersion == other.DCTEncodeVersion + && this.APP14Flags0 == other.APP14Flags0 + && this.APP14Flags1 == other.APP14Flags1 + && this.ColorTransform == other.ColorTransform; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is AdobeMarker && this.Equals((AdobeMarker)obj); + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.DCTEncodeVersion.GetHashCode(), + HashHelpers.Combine( + this.APP14Flags0.GetHashCode(), + HashHelpers.Combine( + this.APP14Flags1.GetHashCode(), + this.ColorTransform.GetHashCode()))); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs new file mode 100644 index 0000000000..86d5957846 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromCmyk : ColorConverters.JpegColorConverter + { + public FromCmyk() + : base(JpegColorSpace.Cmyk) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan cVals = values.Component0; + ReadOnlySpan mVals = values.Component1; + ReadOnlySpan yVals = values.Component2; + ReadOnlySpan kVals = values.Component3; + + var v = new Vector4(0, 0, 0, 1F); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float c = cVals[i]; + float m = mVals[i]; + float y = yVals[i]; + float k = kVals[i] / 255F; + + v.X = c * k; + v.Y = m * k; + v.Z = y * k; + v.W = 1F; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs new file mode 100644 index 0000000000..5c720e61c3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromGrayScale : ColorConverters.JpegColorConverter + { + public FromGrayScale() + : base(JpegColorSpace.GrayScale) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan yVals = values.Component0; + + var v = new Vector4(0, 0, 0, 1); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float y = yVals[i]; + + v.X = y; + v.Y = y; + v.Z = y; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs new file mode 100644 index 0000000000..7f01eedadb --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromRgb : ColorConverters.JpegColorConverter + { + public FromRgb() + : base(JpegColorSpace.RGB) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan rVals = values.Component0; + ReadOnlySpan gVals = values.Component1; + ReadOnlySpan bVals = values.Component2; + + var v = new Vector4(0, 0, 0, 1); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float r = rVals[i]; + float g = gVals[i]; + float b = bVals[i]; + + v.X = r; + v.Y = g; + v.Z = b; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs new file mode 100644 index 0000000000..ddd2197d4a --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromYCbCrBasic : ColorConverters.JpegColorConverter + { + public FromYCbCrBasic() + : base(JpegColorSpace.YCbCr) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + ConvertCore(values, result); + } + + internal static void ConvertCore(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan yVals = values.Component0; + ReadOnlySpan cbVals = values.Component1; + ReadOnlySpan crVals = values.Component2; + + var v = new Vector4(0, 0, 0, 1); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float y = yVals[i]; + float cb = cbVals[i] - 128F; + float cr = crVals[i] - 128F; + + v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); + v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); + v.Z = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs new file mode 100644 index 0000000000..a7fc136afe --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -0,0 +1,115 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Common.Tuples; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromYCbCrSimd : ColorConverters.JpegColorConverter + { + public FromYCbCrSimd() + : base(JpegColorSpace.YCbCr) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + int remainder = result.Length % 8; + int simdCount = result.Length - remainder; + if (simdCount > 0) + { + ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); + } + + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); + } + + /// + /// SIMD convert using buffers of sizes divisable by 8. + /// + internal static void ConvertCore(ComponentValues values, Span result) + { + DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); + + ref Vector4Pair yBase = + ref Unsafe.As(ref values.Component0.DangerousGetPinnableReference()); + ref Vector4Pair cbBase = + ref Unsafe.As(ref values.Component1.DangerousGetPinnableReference()); + ref Vector4Pair crBase = + ref Unsafe.As(ref values.Component2.DangerousGetPinnableReference()); + + ref Vector4Octet resultBase = + ref Unsafe.As(ref result.DangerousGetPinnableReference()); + + var chromaOffset = new Vector4(-128f); + + // Walking 8 elements at one step: + int n = result.Length / 8; + + for (int i = 0; i < n; i++) + { + // y = yVals[i]; + Vector4Pair y = Unsafe.Add(ref yBase, i); + + // cb = cbVals[i] - 128F; + Vector4Pair cb = Unsafe.Add(ref cbBase, i); + cb.AddInplace(chromaOffset); + + // cr = crVals[i] - 128F; + Vector4Pair cr = Unsafe.Add(ref crBase, i); + cr.AddInplace(chromaOffset); + + // r = y + (1.402F * cr); + Vector4Pair r = y; + Vector4Pair tmp = cr; + tmp.MultiplyInplace(1.402F); + r.AddInplace(ref tmp); + + // g = y - (0.344136F * cb) - (0.714136F * cr); + Vector4Pair g = y; + tmp = cb; + tmp.MultiplyInplace(-0.344136F); + g.AddInplace(ref tmp); + tmp = cr; + tmp.MultiplyInplace(-0.714136F); + g.AddInplace(ref tmp); + + // b = y + (1.772F * cb); + Vector4Pair b = y; + tmp = cb; + tmp.MultiplyInplace(1.772F); + b.AddInplace(ref tmp); + + if (Vector.Count == 4) + { + // TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector is terrible?) + r.RoundAndDownscalePreAvx2(); + g.RoundAndDownscalePreAvx2(); + b.RoundAndDownscalePreAvx2(); + } + else if (SimdUtils.IsAvx2CompatibleArchitecture) + { + r.RoundAndDownscaleAvx2(); + g.RoundAndDownscaleAvx2(); + b.RoundAndDownscaleAvx2(); + } + else + { + // TODO: Run fallback scalar code here + // However, no issues expected before someone implements this: https://github.com/dotnet/coreclr/issues/12007 + throw new NotImplementedException("Your CPU architecture is too modern!"); + } + + // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: + ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); + destination.Collect(ref r, ref g, ref b); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs new file mode 100644 index 0000000000..77e74c32b0 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Common.Tuples; + +// ReSharper disable ImpureMethodCallOnReadonlyValueField +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromYCbCrSimdAvx2 : ColorConverters.JpegColorConverter + { + public FromYCbCrSimdAvx2() + : base(JpegColorSpace.YCbCr) + { + } + + public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.IsAvx2CompatibleArchitecture; + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + int remainder = result.Length % 8; + int simdCount = result.Length - remainder; + if (simdCount > 0) + { + ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); + } + + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); + } + + /// + /// SIMD convert using buffers of sizes divisable by 8. + /// + internal static void ConvertCore(ComponentValues values, Span result) + { + // This implementation is actually AVX specific. + // An AVX register is capable of storing 8 float-s. + if (!IsAvailable) + { + throw new InvalidOperationException( + "JpegColorConverter.FromYCbCrSimd256 can be used only on architecture having 256 byte floating point SIMD registers!"); + } + + ref Vector yBase = + ref Unsafe.As>(ref values.Component0.DangerousGetPinnableReference()); + ref Vector cbBase = + ref Unsafe.As>(ref values.Component1.DangerousGetPinnableReference()); + ref Vector crBase = + ref Unsafe.As>(ref values.Component2.DangerousGetPinnableReference()); + + ref Vector4Octet resultBase = + ref Unsafe.As(ref result.DangerousGetPinnableReference()); + + var chromaOffset = new Vector(-128f); + + // Walking 8 elements at one step: + int n = result.Length / 8; + + var rr = default(Vector4Pair); + var gg = default(Vector4Pair); + var bb = default(Vector4Pair); + + ref Vector rrRefAsVector = ref Unsafe.As>(ref rr); + ref Vector ggRefAsVector = ref Unsafe.As>(ref gg); + ref Vector bbRefAsVector = ref Unsafe.As>(ref bb); + + var scale = new Vector(1 / 255f); + + for (int i = 0; i < n; i++) + { + // y = yVals[i]; + // cb = cbVals[i] - 128F; + // cr = crVals[i] - 128F; + Vector y = Unsafe.Add(ref yBase, i); + Vector cb = Unsafe.Add(ref cbBase, i) + chromaOffset; + Vector cr = Unsafe.Add(ref crBase, i) + chromaOffset; + + // r = y + (1.402F * cr); + // g = y - (0.344136F * cb) - (0.714136F * cr); + // b = y + (1.772F * cb); + // Adding & multiplying 8 elements at one time: + Vector r = y + (cr * new Vector(1.402F)); + Vector g = y - (cb * new Vector(0.344136F)) - (cr * new Vector(0.714136F)); + Vector b = y + (cb * new Vector(1.772F)); + + r = r.FastRound(); + g = g.FastRound(); + b = b.FastRound(); + r *= scale; + g *= scale; + b *= scale; + + rrRefAsVector = r; + ggRefAsVector = g; + bbRefAsVector = b; + + // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: + ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); + destination.Collect(ref rr, ref gg, ref bb); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs new file mode 100644 index 0000000000..6d8e6ef5a9 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + internal abstract partial class JpegColorConverter + { + internal class FromYccK : JpegColorConverter + { + public FromYccK() + : base(JpegColorSpace.Ycck) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan yVals = values.Component0; + ReadOnlySpan cbVals = values.Component1; + ReadOnlySpan crVals = values.Component2; + ReadOnlySpan kVals = values.Component3; + + var v = new Vector4(0, 0, 0, 1F); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float y = yVals[i]; + float cb = cbVals[i] - 128F; + float cr = crVals[i] - 128F; + float k = kVals[i] / 255F; + + v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; + v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k; + v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; + v.W = 1F; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs new file mode 100644 index 0000000000..e0abc3215c --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -0,0 +1,201 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using SixLabors.ImageSharp.Common.Tuples; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters +{ + /// + /// Encapsulates the conversion of Jpeg channels to RGBA values packed in buffer. + /// + internal abstract partial class JpegColorConverter + { + /// + /// The avalilable converters + /// + private static readonly JpegColorConverter[] Converters = + { + GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() + }; + + /// + /// Initializes a new instance of the class. + /// + protected JpegColorConverter(JpegColorSpace colorSpace) + { + this.ColorSpace = colorSpace; + } + + /// + /// Gets the of this converter. + /// + public JpegColorSpace ColorSpace { get; } + + /// + /// Returns the corresponding to the given + /// + public static JpegColorConverter GetConverter(JpegColorSpace colorSpace) + { + JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace); + if (converter == null) + { + throw new Exception($"Could not find any converter for JpegColorSpace {colorSpace}!"); + } + + return converter; + } + + /// + /// He implementation of the conversion. + /// + /// The input as a stack-only struct + /// The destination buffer of values + public abstract void ConvertToRGBA(ComponentValues values, Span result); + + /// + /// Returns the for the YCbCr colorspace that matches the current CPU architecture. + /// + private static JpegColorConverter GetYCbCrConverter() => + FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); + + /// + /// A stack-only struct to reference the input buffers using -s. + /// + public struct ComponentValues + { + /// + /// The component count + /// + public readonly int ComponentCount; + + /// + /// The component 0 (eg. Y) + /// + public readonly ReadOnlySpan Component0; + + /// + /// The component 1 (eg. Cb) + /// + public readonly ReadOnlySpan Component1; + + /// + /// The component 2 (eg. Cr) + /// + public readonly ReadOnlySpan Component2; + + /// + /// The component 4 + /// + public readonly ReadOnlySpan Component3; + + /// + /// Initializes a new instance of the struct. + /// + /// The 1-4 sized list of component buffers. + /// The row to convert + public ComponentValues(IReadOnlyList> componentBuffers, int row) + { + this.ComponentCount = componentBuffers.Count; + + this.Component0 = componentBuffers[0].GetRowSpan(row); + this.Component1 = Span.Empty; + this.Component2 = Span.Empty; + this.Component3 = Span.Empty; + + if (this.ComponentCount > 1) + { + this.Component1 = componentBuffers[1].GetRowSpan(row); + if (this.ComponentCount > 2) + { + this.Component2 = componentBuffers[2].GetRowSpan(row); + if (this.ComponentCount > 3) + { + this.Component3 = componentBuffers[3].GetRowSpan(row); + } + } + } + } + + private ComponentValues( + int componentCount, + ReadOnlySpan c0, + ReadOnlySpan c1, + ReadOnlySpan c2, + ReadOnlySpan c3) + { + this.ComponentCount = componentCount; + this.Component0 = c0; + this.Component1 = c1; + this.Component2 = c2; + this.Component3 = c3; + } + + public ComponentValues Slice(int start, int length) + { + ReadOnlySpan c0 = this.Component0.Slice(start, length); + ReadOnlySpan c1 = this.ComponentCount > 1 ? this.Component1.Slice(start, length) : ReadOnlySpan.Empty; + ReadOnlySpan c2 = this.ComponentCount > 2 ? this.Component2.Slice(start, length) : ReadOnlySpan.Empty; + ReadOnlySpan c3 = this.ComponentCount > 3 ? this.Component3.Slice(start, length) : ReadOnlySpan.Empty; + + return new ComponentValues(this.ComponentCount, c0, c1, c2, c3); + } + } + + internal struct Vector4Octet + { +#pragma warning disable SA1132 // Do not combine fields + public Vector4 V0, V1, V2, V3, V4, V5, V6, V7; + + /// + /// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order. + /// + public void Collect(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b) + { + this.V0.X = r.A.X; + this.V0.Y = g.A.X; + this.V0.Z = b.A.X; + this.V0.W = 1f; + + this.V1.X = r.A.Y; + this.V1.Y = g.A.Y; + this.V1.Z = b.A.Y; + this.V1.W = 1f; + + this.V2.X = r.A.Z; + this.V2.Y = g.A.Z; + this.V2.Z = b.A.Z; + this.V2.W = 1f; + + this.V3.X = r.A.W; + this.V3.Y = g.A.W; + this.V3.Z = b.A.W; + this.V3.W = 1f; + + this.V4.X = r.B.X; + this.V4.Y = g.B.X; + this.V4.Z = b.B.X; + this.V4.W = 1f; + + this.V5.X = r.B.Y; + this.V5.Y = g.B.Y; + this.V5.Z = b.B.Y; + this.V5.W = 1f; + + this.V6.X = r.B.Z; + this.V6.Y = g.B.Z; + this.V6.Z = b.B.Z; + this.V6.W = 1f; + + this.V7.X = r.B.W; + this.V7.Y = g.B.W; + this.V7.Z = b.B.W; + this.V7.W = 1f; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs new file mode 100644 index 0000000000..da97f9e2ae --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs @@ -0,0 +1,16 @@ +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Various utilities for . + /// + internal static class ComponentUtils + { + /// + /// Gets a reference to the at the given row and column index from + /// + public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by) + { + return ref component.SpectralBlocks[bx, by]; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs new file mode 100644 index 0000000000..4109fc10e7 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs @@ -0,0 +1,46 @@ +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Common interface to represent raw Jpeg components. + /// + internal interface IJpegComponent + { + /// + /// Gets the component's position in the components array. + /// + int Index { get; } + + /// + /// Gets the number of blocks in this component as + /// + Size SizeInBlocks { get; } + + /// + /// Gets the horizontal and the vertical sampling factor as + /// + Size SamplingFactors { get; } + + /// + /// Gets the divisors needed to apply when calculating colors. + /// + /// https://en.wikipedia.org/wiki/Chroma_subsampling + /// + /// In case of 4:2:0 subsampling the values are: Luma.SubSamplingDivisors = (1,1) Chroma.SubSamplingDivisors = (2,2) + /// + Size SubSamplingDivisors { get; } + + /// + /// Gets the index of the quantization table for this block. + /// + int QuantizationTableIndex { get; } + + /// + /// Gets the storing the "raw" frequency-domain decoded + unzigged blocks. + /// We need to apply IDCT and dequantiazition to transform them into color-space blocks. + /// + Buffer2D SpectralBlocks { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs new file mode 100644 index 0000000000..3873656a4e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// + /// Represents decompressed, unprocessed jpeg data with spectral space -s. + /// + internal interface IRawJpegData : IDisposable + { + /// + /// Gets the image size in pixels. + /// + Size ImageSizeInPixels { get; } + + /// + /// Gets the number of coponents. + /// + int ComponentCount { get; } + + /// + /// Gets the color space + /// + JpegColorSpace ColorSpace { get; } + + /// + /// Gets the components. + /// + IEnumerable Components { get; } + + /// + /// Gets the quantization tables, in zigzag order. + /// + Block8x8F[] QuantizationTables { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs new file mode 100644 index 0000000000..cba7be5539 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs @@ -0,0 +1,127 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Provides information about the JFIF marker segment + /// TODO: Thumbnail? + /// + internal struct JFifMarker : IEquatable + { + /// + /// Gets the length of an JFIF marker segment. + /// + public const int Length = 13; + + /// + /// Initializes a new instance of the struct. + /// + /// The major version + /// The minor version + /// The units for the density values + /// The horizontal pixel density + /// The veritcal pixel density + private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) + { + Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity)); + Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity)); + + this.MajorVersion = majorVersion; + this.MinorVersion = minorVersion; + this.DensityUnits = densityUnits; + this.XDensity = xDensity; + this.YDensity = yDensity; + } + + /// + /// Gets the major version + /// + public byte MajorVersion { get; } + + /// + /// Gets the minor version + /// + public byte MinorVersion { get; } + + /// + /// Gets the units for the following pixel density fields + /// 00 : No units; width:height pixel aspect ratio = Ydensity:Xdensity + /// 01 : Pixels per inch (2.54 cm) + /// 02 : Pixels per centimeter + /// + public byte DensityUnits { get; } + + /// + /// Gets the horizontal pixel density. Must not be zero. + /// + public short XDensity { get; } + + /// + /// Gets the vertical pixel density. Must not be zero. + /// + public short YDensity { get; } + + /// + /// Converts the specified byte array representation of an JFIF marker to its equivalent and + /// returns a value that indicates whether the conversion succeeded. + /// + /// The byte array containing metadata to parse + /// The marker to return. + public static bool TryParse(byte[] bytes, out JFifMarker marker) + { + if (ProfileResolver.IsProfile(bytes, ProfileResolver.JFifMarker)) + { + byte majorVersion = bytes[5]; + byte minorVersion = bytes[6]; + byte densityUnits = bytes[7]; + short xDensity = (short)((bytes[8] << 8) | bytes[9]); + short yDensity = (short)((bytes[10] << 8) | bytes[11]); + + if (xDensity > 0 && yDensity > 0) + { + marker = new JFifMarker(majorVersion, minorVersion, densityUnits, xDensity, yDensity); + return true; + } + } + + marker = default(JFifMarker); + return false; + } + + /// + public bool Equals(JFifMarker other) + { + return this.MajorVersion == other.MajorVersion + && this.MinorVersion == other.MinorVersion + && this.DensityUnits == other.DensityUnits + && this.XDensity == other.XDensity + && this.YDensity == other.YDensity; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is JFifMarker && this.Equals((JFifMarker)obj); + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.MajorVersion.GetHashCode(), + HashHelpers.Combine( + this.MinorVersion.GetHashCode(), + HashHelpers.Combine( + this.DensityUnits.GetHashCode(), + HashHelpers.Combine(this.XDensity, this.YDensity)))); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs new file mode 100644 index 0000000000..574967b6bf --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct JpegBlockPostProcessor + { + /// + /// Source block + /// + public Block8x8F SourceBlock; + + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F WorkspaceBlock1; + + /// + /// Temporal block 2 to store intermediate and/or final computation results + /// + public Block8x8F WorkspaceBlock2; + + /// + /// The quantization table as + /// + public Block8x8F DequantiazationTable; + + /// + /// Defines the horizontal and vertical scale we need to apply to the 8x8 sized block. + /// + private Size subSamplingDivisors; + + /// + /// Initializes a new instance of the struct. + /// + public JpegBlockPostProcessor(IRawJpegData decoder, IJpegComponent component) + { + int qtIndex = component.QuantizationTableIndex; + this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]); + this.subSamplingDivisors = component.SubSamplingDivisors; + + this.SourceBlock = default(Block8x8F); + this.WorkspaceBlock1 = default(Block8x8F); + this.WorkspaceBlock2 = default(Block8x8F); + } + + /// + /// Processes 'sourceBlock' producing Jpeg color channel values from spectral values: + /// - Dequantize + /// - Applying IDCT + /// - Level shift by +128, clip to [0, 255] + /// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in + /// + public void ProcessBlockColorsInto( + ref Block8x8 sourceBlock, + BufferArea destArea) + { + ref Block8x8F b = ref this.SourceBlock; + b.LoadFrom(ref sourceBlock); + + // Dequantize: + b.MultiplyInplace(ref this.DequantiazationTable); + + FastFloatingPointDCT.TransformIDCT(ref b, ref this.WorkspaceBlock1, ref this.WorkspaceBlock2); + + // 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! + this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(); + + this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs new file mode 100644 index 0000000000..da353d2795 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs @@ -0,0 +1,20 @@ +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Identifies the colorspace of a Jpeg image + /// + internal enum JpegColorSpace + { + Undefined = 0, + + GrayScale, + + Ycck, + + Cmyk, + + RGB, + + YCbCr + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs new file mode 100644 index 0000000000..87c1431e02 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs @@ -0,0 +1,104 @@ +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates postprocessing data for one component for . + /// + internal class JpegComponentPostProcessor : IDisposable + { + /// + /// Points to the current row in . + /// + private int currentComponentRowInBlocks; + + /// + /// The size of the area in corrsponding to one 8x8 Jpeg block + /// + private readonly Size blockAreaSize; + + /// + /// Initializes a new instance of the class. + /// + public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component) + { + this.Component = component; + this.ImagePostProcessor = imagePostProcessor; + this.ColorBuffer = new Buffer2D(imagePostProcessor.PostProcessorBufferSize); + + this.BlockRowsPerStep = JpegImagePostProcessor.BlockRowsPerStep / this.Component.SubSamplingDivisors.Height; + this.blockAreaSize = this.Component.SubSamplingDivisors * 8; + } + + /// + /// Gets the + /// + public JpegImagePostProcessor ImagePostProcessor { get; } + + /// + /// Gets the + /// + public IJpegComponent Component { get; } + + /// + /// Gets the temporal working buffer of color values. + /// + public Buffer2D ColorBuffer { get; } + + /// + /// Gets + /// + public Size SizeInBlocks => this.Component.SizeInBlocks; + + /// + /// Gets the maximal number of block rows being processed in one step. + /// + public int BlockRowsPerStep { get; } + + /// + public void Dispose() + { + this.ColorBuffer.Dispose(); + } + + /// + /// Invoke for block rows, copy the result into . + /// + public void CopyBlocksToColorBuffer() + { + var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component); + + for (int y = 0; y < this.BlockRowsPerStep; y++) + { + int yBlock = this.currentComponentRowInBlocks + y; + + if (yBlock >= this.SizeInBlocks.Height) + { + break; + } + + int yBuffer = y * this.blockAreaSize.Height; + + for (int x = 0; x < this.SizeInBlocks.Width; x++) + { + int xBlock = x; + int xBuffer = x * this.blockAreaSize.Width; + + ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock); + + BufferArea destArea = this.ColorBuffer.GetArea( + xBuffer, + yBuffer, + this.blockAreaSize.Width, + this.blockAreaSize.Height); + + blockPp.ProcessBlockColorsInto(ref block, destArea); + } + } + + this.currentComponentRowInBlocks += this.BlockRowsPerStep; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs new file mode 100644 index 0000000000..125ec52723 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -0,0 +1,160 @@ +using System; +using System.Linq; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates the execution od post-processing algorithms to be applied on a to produce a valid :
+ /// (1) Dequantization
+ /// (2) IDCT
+ /// (3) Color conversion form one of the -s into a buffer of RGBA values
+ /// (4) Packing pixels from the buffer.
+ /// These operations are executed in steps. + /// image rows are converted in one step, + /// which means that size of the allocated memory is limited (does not depend on ). + ///
+ internal class JpegImagePostProcessor : IDisposable + { + /// + /// The number of block rows to be processed in one Step. + /// + public const int BlockRowsPerStep = 4; + + /// + /// The number of image pixel rows to be processed in one step. + /// + public const int PixelRowsPerStep = 4 * 8; + + /// + /// Temporal buffer to store a row of colors. + /// + private readonly Buffer rgbaBuffer; + + /// + /// The corresponding to the current determined by . + /// + private ColorConverters.JpegColorConverter colorConverter; + + /// + /// Initializes a new instance of the class. + /// + /// The representing the uncompressed spectral Jpeg data + public JpegImagePostProcessor(IRawJpegData rawJpeg) + { + this.RawJpeg = rawJpeg; + IJpegComponent c0 = rawJpeg.Components.First(); + this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; + this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); + + this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray(); + this.rgbaBuffer = new Buffer(rawJpeg.ImageSizeInPixels.Width); + this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace); + } + + /// + /// Gets the instances. + /// + public JpegComponentPostProcessor[] ComponentProcessors { get; } + + /// + /// Gets the to be processed. + /// + public IRawJpegData RawJpeg { get; } + + /// + /// Gets the total number of post processor steps deduced from the height of the image and . + /// + public int NumberOfPostProcessorSteps { get; } + + /// + /// Gets the size of the temporal buffers we need to allocate into . + /// + public Size PostProcessorBufferSize { get; } + + /// + /// Gets the value of the counter that grows by each step by . + /// + public int PixelRowCounter { get; private set; } + + /// + public void Dispose() + { + foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) + { + cpp.Dispose(); + } + + this.rgbaBuffer.Dispose(); + } + + /// + /// Process all pixels into 'destination'. The image dimensions should match . + /// + /// The pixel type + /// The destination image + public void PostProcess(ImageFrame destination) + where TPixel : struct, IPixel + { + this.PixelRowCounter = 0; + + if (this.RawJpeg.ImageSizeInPixels != destination.Size()) + { + throw new ArgumentException("Input image is not of the size of the processed one!"); + } + + while (this.PixelRowCounter < this.RawJpeg.ImageSizeInPixels.Height) + { + this.DoPostProcessorStep(destination); + } + } + + /// + /// Execute one step rocessing pixel rows into 'destination'. + /// + /// The pixel type + /// The destination image. + public void DoPostProcessorStep(ImageFrame destination) + where TPixel : struct, IPixel + { + foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) + { + cpp.CopyBlocksToColorBuffer(); + } + + this.ConvertColorsInto(destination); + + this.PixelRowCounter += PixelRowsPerStep; + } + + /// + /// Convert and copy row of colors into 'destination' starting at row . + /// + /// The pixel type + /// The destination image + private void ConvertColorsInto(ImageFrame destination) + where TPixel : struct, IPixel + { + int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep); + + Buffer2D[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray(); + + for (int yy = this.PixelRowCounter; yy < maxY; yy++) + { + int y = yy - this.PixelRowCounter; + + var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y); + this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer); + + Span destRow = destination.GetPixelRowSpan(yy); + + PixelOperations.Instance.PackFromVector4(this.rgbaBuffer, destRow, destination.Width); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs new file mode 100644 index 0000000000..7ea0f9215e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Provides methods for identifying metadata and color profiles within jpeg images. + /// + internal static class ProfileResolver + { + /// + /// Describes the EXIF specific markers + /// + public static readonly byte[] JFifMarker = ToAsciiBytes("JFIF\0"); + + /// + /// Describes the EXIF specific markers + /// + public static readonly byte[] IccMarker = ToAsciiBytes("ICC_PROFILE\0"); + + /// + /// Describes the ICC specific markers + /// + public static readonly byte[] ExifMarker = ToAsciiBytes("Exif\0\0"); + + /// + /// Describes Adobe specific markers + /// + public static readonly byte[] AdobeMarker = ToAsciiBytes("Adobe"); + + /// + /// Returns a value indicating whether the passed bytes are a match to the profile identifer + /// + /// The bytes to check + /// The profile identifier + /// The + public static bool IsProfile(Span bytesToCheck, Span profileIdentifier) + { + return bytesToCheck.Length >= profileIdentifier.Length + && bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier); + } + + // No Encoding.ASCII nor Linq.Select on NetStandard 1.1 + private static byte[] ToAsciiBytes(string str) + { + int length = str.Length; + byte[] bytes = new byte[length]; + char[] chars = str.ToCharArray(); + for (int i = 0; i < length; i++) + { + bytes[i] = (byte)chars[i]; + } + + return bytes; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/DCT.cs b/src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs similarity index 86% rename from src/ImageSharp/Formats/Jpeg/Components/DCT.cs rename to src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs index 5729fe46d6..3ee6e72c5d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/DCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs @@ -1,57 +1,59 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpg -{ - using System.Numerics; - using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ /// - /// Contains forward and inverse DCT implementations + /// Contains inaccurate, but fast forward and inverse DCT implementations. /// - internal static class DCT + internal static class FastFloatingPointDCT { #pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore - private static readonly float C_1_175876 = 1.175876f; + private const float C_1_175876 = 1.175875602f; - private static readonly float C_1_961571 = -1.961571f; + private const float C_1_961571 = -1.961570560f; - private static readonly float C_0_390181 = -0.390181f; + private const float C_0_390181 = -0.390180644f; - private static readonly float C_0_899976 = -0.899976f; + private const float C_0_899976 = -0.899976223f; - private static readonly float C_2_562915 = -2.562915f; + private const float C_2_562915 = -2.562915447f; - private static readonly float C_0_298631 = 0.298631f; + private const float C_0_298631 = 0.298631336f; - private static readonly float C_2_053120 = 2.053120f; + private const float C_2_053120 = 2.053119869f; - private static readonly float C_3_072711 = 3.072711f; + private const float C_3_072711 = 3.072711026f; - private static readonly float C_1_501321 = 1.501321f; + private const float C_1_501321 = 1.501321110f; - private static readonly float C_0_541196 = 0.541196f; + private const float C_0_541196 = 0.541196100f; - private static readonly float C_1_847759 = -1.847759f; + private const float C_1_847759 = -1.847759065f; - private static readonly float C_0_765367 = 0.765367f; + private const float C_0_765367 = 0.765366865f; - private static readonly float C_0_125 = 0.1250f; + private const float C_0_125 = 0.1250f; #pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); /// - /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization) + /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization). + /// Ported from https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 /// /// Source /// Destination /// Temporary block provided by the caller public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp) { + // TODO: Transpose is a bottleneck now. We need full AVX support to optimize it: + // https://github.com/dotnet/corefx/issues/22940 src.TransposeInto(ref temp); + IDCT8x4_LeftPart(ref temp, ref dest); IDCT8x4_RightPart(ref temp, ref dest); @@ -60,7 +62,8 @@ namespace ImageSharp.Formats.Jpg IDCT8x4_LeftPart(ref temp, ref dest); IDCT8x4_RightPart(ref temp, ref dest); - dest.MultiplyAllInplace(C_0_125); + // TODO: What if we leave the blocks in a scaled-by-x8 state until final color packing? + dest.MultiplyInplace(C_0_125); } /// @@ -334,7 +337,7 @@ namespace ImageSharp.Formats.Jpg FDCT8x4_LeftPart(ref temp, ref dest); FDCT8x4_RightPart(ref temp, ref dest); - dest.MultiplyAllInplace(C_0_125); + dest.MultiplyInplace(C_0_125); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs new file mode 100644 index 0000000000..978688673f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + /// + /// Extension methods for + /// + internal static class SizeExtensions + { + /// + /// Multiplies 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'. + /// TODO: Shouldn't we expose this as operator in SixLabors.Core? + /// + public static Size MultiplyBy(this Size a, Size b) => new Size(a.Width * b.Width, a.Height * b.Height); + + /// + /// Divides 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'. + /// TODO: Shouldn't we expose this as operator in SixLabors.Core? + /// + public static Size DivideBy(this Size a, Size b) => new Size(a.Width / b.Width, a.Height / b.Height); + + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// + public static Size DivideRoundUp(this Size originalSize, int divX, int divY) + { + var sizeVect = (Vector2)(SizeF)originalSize; + sizeVect /= new Vector2(divX, divY); + sizeVect.X = MathF.Ceiling(sizeVect.X); + sizeVect.Y = MathF.Ceiling(sizeVect.Y); + + return new Size((int)sizeVect.X, (int)sizeVect.Y); + } + + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// + public static Size DivideRoundUp(this Size originalSize, int divisor) => + DivideRoundUp(originalSize, divisor, divisor); + + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// + public static Size DivideRoundUp(this Size originalSize, Size divisor) => + DivideRoundUp(originalSize, divisor.Width, divisor.Height); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/UnzigData.cs b/src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs similarity index 64% rename from src/ImageSharp/Formats/Jpeg/UnzigData.cs rename to src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs index e74dd5c73c..18270f5bad 100644 --- a/src/ImageSharp/Formats/Jpeg/UnzigData.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ /// /// Holds the Jpeg UnZig array in a value/stack type. /// Unzig maps from the zigzag ordering to the natural ordering. For example, /// unzig[3] is the column and row of the fourth element in zigzag order. The /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). /// - internal unsafe struct UnzigData + internal unsafe struct ZigZag { /// /// Copy of in a value type @@ -34,15 +32,30 @@ namespace ImageSharp.Formats }; /// - /// Creates and fills an instance of with Jpeg unzig indices + /// Creates and fills an instance of with Jpeg unzig indices /// /// The new instance - public static UnzigData Create() + public static ZigZag CreateUnzigTable() { - UnzigData result = default(UnzigData); + ZigZag result = default(ZigZag); int* unzigPtr = result.Data; Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); return result; } + + /// + /// Apply Zigging to the given quantization table, so it will be sufficient to multiply blocks for dequantizing them. + /// + public static Block8x8F CreateDequantizationTable(ref Block8x8F qt) + { + Block8x8F result = default(Block8x8F); + + for (int i = 0; i < 64; i++) + { + result[Unzig[i]] = qt[i]; + } + + return result; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs deleted file mode 100644 index f84dc977f1..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -// -#pragma warning disable -namespace ImageSharp.Formats.Jpg -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - internal partial struct Block8x8F - { - private static readonly Vector4 CMin4 = new Vector4(0F); - private static readonly Vector4 CMax4 = new Vector4(255F); - private static readonly Vector4 COff4 = new Vector4(128F); - - /// - /// Transpose the block into the destination block. - /// - /// The destination block - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void TransposeInto(ref Block8x8F d) - { - d.V0L.X = V0L.X; - d.V1L.X = V0L.Y; - d.V2L.X = V0L.Z; - d.V3L.X = V0L.W; - d.V4L.X = V0R.X; - d.V5L.X = V0R.Y; - d.V6L.X = V0R.Z; - d.V7L.X = V0R.W; - - d.V0L.Y = V1L.X; - d.V1L.Y = V1L.Y; - d.V2L.Y = V1L.Z; - d.V3L.Y = V1L.W; - d.V4L.Y = V1R.X; - d.V5L.Y = V1R.Y; - d.V6L.Y = V1R.Z; - d.V7L.Y = V1R.W; - - d.V0L.Z = V2L.X; - d.V1L.Z = V2L.Y; - d.V2L.Z = V2L.Z; - d.V3L.Z = V2L.W; - d.V4L.Z = V2R.X; - d.V5L.Z = V2R.Y; - d.V6L.Z = V2R.Z; - d.V7L.Z = V2R.W; - - d.V0L.W = V3L.X; - d.V1L.W = V3L.Y; - d.V2L.W = V3L.Z; - d.V3L.W = V3L.W; - d.V4L.W = V3R.X; - d.V5L.W = V3R.Y; - d.V6L.W = V3R.Z; - d.V7L.W = V3R.W; - - d.V0R.X = V4L.X; - d.V1R.X = V4L.Y; - d.V2R.X = V4L.Z; - d.V3R.X = V4L.W; - d.V4R.X = V4R.X; - d.V5R.X = V4R.Y; - d.V6R.X = V4R.Z; - d.V7R.X = V4R.W; - - d.V0R.Y = V5L.X; - d.V1R.Y = V5L.Y; - d.V2R.Y = V5L.Z; - d.V3R.Y = V5L.W; - d.V4R.Y = V5R.X; - d.V5R.Y = V5R.Y; - d.V6R.Y = V5R.Z; - d.V7R.Y = V5R.W; - - d.V0R.Z = V6L.X; - d.V1R.Z = V6L.Y; - d.V2R.Z = V6L.Z; - d.V3R.Z = V6L.W; - d.V4R.Z = V6R.X; - d.V5R.Z = V6R.Y; - d.V6R.Z = V6R.Z; - d.V7R.Z = V6R.W; - - d.V0R.W = V7L.X; - d.V1R.W = V7L.Y; - d.V2R.W = V7L.Z; - d.V3R.W = V7L.W; - d.V4R.W = V7R.X; - d.V5R.W = V7R.Y; - d.V6R.W = V7R.Z; - d.V7R.W = V7R.W; - } - - /// - /// Level shift by +128, clip to [0, 255] - /// - /// The destination block - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) - { - d.V0L = Vector4.Clamp(V0L + COff4, CMin4, CMax4); - d.V0R = Vector4.Clamp(V0R + COff4, CMin4, CMax4); - d.V1L = Vector4.Clamp(V1L + COff4, CMin4, CMax4); - d.V1R = Vector4.Clamp(V1R + COff4, CMin4, CMax4); - d.V2L = Vector4.Clamp(V2L + COff4, CMin4, CMax4); - d.V2R = Vector4.Clamp(V2R + COff4, CMin4, CMax4); - d.V3L = Vector4.Clamp(V3L + COff4, CMin4, CMax4); - d.V3R = Vector4.Clamp(V3R + COff4, CMin4, CMax4); - d.V4L = Vector4.Clamp(V4L + COff4, CMin4, CMax4); - d.V4R = Vector4.Clamp(V4R + COff4, CMin4, CMax4); - d.V5L = Vector4.Clamp(V5L + COff4, CMin4, CMax4); - d.V5R = Vector4.Clamp(V5R + COff4, CMin4, CMax4); - d.V6L = Vector4.Clamp(V6L + COff4, CMin4, CMax4); - d.V6R = Vector4.Clamp(V6R + COff4, CMin4, CMax4); - d.V7L = Vector4.Clamp(V7L + COff4, CMin4, CMax4); - d.V7R = Vector4.Clamp(V7R + COff4, CMin4, CMax4); - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt deleted file mode 100644 index 03566acbbc..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -<#@ template debug="false" hostspecific="false" language="C#" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ output extension=".cs" #> -// -#pragma warning disable -<# -char[] coordz = {'X', 'Y', 'Z', 'W'}; -#> -namespace ImageSharp.Formats.Jpg -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - internal partial struct Block8x8F - { - private static readonly Vector4 CMin4 = new Vector4(0F); - private static readonly Vector4 CMax4 = new Vector4(255F); - private static readonly Vector4 COff4 = new Vector4(128F); - - /// - /// Transpose the block into the destination block. - /// - /// The destination block - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void TransposeInto(ref Block8x8F d) - { - <# - PushIndent(" "); - - for (int i = 0; i < 8; i++) - { - char destCoord = coordz[i % 4]; - char destSide = (i / 4) % 2 == 0 ? 'L' : 'R'; - - for (int j = 0; j < 8; j++) - { - if(i > 0 && j == 0){ - WriteLine(""); - } - - char srcCoord = coordz[j % 4]; - char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; - - string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n"; - Write(expression); - } - } - PopIndent(); - #> - } - - /// - /// Level shift by +128, clip to [0, 255] - /// - /// The destination block - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) - { - <# - - PushIndent(" "); - - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 2; j++) - { - char side = j == 0 ? 'L' : 'R'; - Write($"d.V{i}{side} = Vector4.Clamp(V{i}{side} + COff4, CMin4, CMax4);\r\n"); - } - } - PopIndent(); - #> - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Component.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/Component.cs deleted file mode 100644 index 5b53db1901..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Component.cs +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - /// - /// Represents a single color component - /// - internal struct Component - { - /// - /// Gets or sets the horizontal sampling factor. - /// - public int HorizontalFactor; - - /// - /// Gets or sets the vertical sampling factor. - /// - public int VerticalFactor; - - /// - /// Gets or sets the identifier - /// - public byte Identifier; - - /// - /// Gets or sets the quantization table destination selector. - /// - public byte Selector; - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlock.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlock.cs deleted file mode 100644 index 900d77ec46..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlock.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System; - - /// - /// A structure to store unprocessed instances and their coordinates while scanning the image. - /// The is present in a "raw" decoded frequency-domain form. - /// We need to apply IDCT and unzigging to transform them into color-space blocks. - /// - internal struct DecodedBlock - { - /// - /// A value indicating whether the instance is initialized. - /// - public bool Initialized; - - /// - /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - public int Bx; - - /// - /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - public int By; - - /// - /// The - /// - public Block8x8F Block; - - /// - /// Store the block data into a - /// - /// X coordinate of the block - /// Y coordinate of the block - /// The - public void SaveBlock(int bx, int by, ref Block8x8F block) - { - this.Bx = bx; - this.By = by; - this.Block = block; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs deleted file mode 100644 index 71472c00fc..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs +++ /dev/null @@ -1,168 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using ImageSharp.Memory; - - /// - /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. - /// - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct JpegBlockProcessor - { - /// - /// The - /// - private ComputationData data; - - /// - /// Pointers to elements of - /// - private DataPointers pointers; - - /// - /// The component index. - /// - private int componentIndex; - - /// - /// Initialize the instance on the stack. - /// - /// The instance - /// The current component index - public static void Init(JpegBlockProcessor* processor, int componentIndex) - { - processor->componentIndex = componentIndex; - processor->data = ComputationData.Create(); - processor->pointers = new DataPointers(&processor->data); - } - - /// - /// Dequantize, perform the inverse DCT and store the blocks to the into the corresponding instances. - /// - /// The instance - public void ProcessAllBlocks(JpegDecoderCore decoder) - { - Buffer blockArray = decoder.DecodedBlocks[this.componentIndex]; - for (int i = 0; i < blockArray.Length; i++) - { - this.ProcessBlockColors(decoder, ref blockArray[i]); - } - } - - /// - /// Dequantize, perform the inverse DCT and store decodedBlock.Block to the into the corresponding instance. - /// - /// The - /// The - private void ProcessBlockColors(JpegDecoderCore decoder, ref DecodedBlock decodedBlock) - { - this.data.Block = decodedBlock.Block; - int qtIndex = decoder.ComponentArray[this.componentIndex].Selector; - this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; - - Block8x8F* b = this.pointers.Block; - - Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); - - DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); - - JpegPixelArea destChannel = decoder.GetDestinationChannel(this.componentIndex); - JpegPixelArea destArea = destChannel.GetOffsetedSubAreaForBlock(decodedBlock.Bx, decodedBlock.By); - destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); - } - - /// - /// Holds the "large" data blocks needed for computations. - /// - [StructLayout(LayoutKind.Sequential)] - public struct ComputationData - { - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Block; - - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Temp1; - - /// - /// Temporal block 2 to store intermediate and/or final computation results - /// - public Block8x8F Temp2; - - /// - /// The quantization table as - /// - public Block8x8F QuantiazationTable; - - /// - /// The jpeg unzig data - /// - public UnzigData Unzig; - - /// - /// Creates and initializes a new instance - /// - /// The - public static ComputationData Create() - { - ComputationData data = default(ComputationData); - data.Unzig = UnzigData.Create(); - return data; - } - } - - /// - /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of - /// - public struct DataPointers - { - /// - /// Pointer to - /// - public Block8x8F* Block; - - /// - /// Pointer to - /// - public Block8x8F* Temp1; - - /// - /// Pointer to - /// - public Block8x8F* Temp2; - - /// - /// Pointer to - /// - public Block8x8F* QuantiazationTable; - - /// - /// Pointer to as int* - /// - public int* Unzig; - - /// - /// Initializes a new instance of the struct. - /// - /// Pointer to - internal DataPointers(ComputationData* dataPtr) - { - this.Block = &dataPtr->Block; - this.Temp1 = &dataPtr->Temp1; - this.Temp2 = &dataPtr->Temp2; - this.QuantiazationTable = &dataPtr->QuantiazationTable; - this.Unzig = dataPtr->Unzig.Data; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs deleted file mode 100644 index 342ce299ce..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs +++ /dev/null @@ -1,115 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - - /// - /// Represents an area of a Jpeg subimage (channel) - /// - internal struct JpegPixelArea - { - /// - /// Initializes a new instance of the struct from existing data. - /// - /// The pixel buffer - /// The stride - /// The offset - public JpegPixelArea(Buffer2D pixels, int stride, int offset) - { - this.Stride = stride; - this.Pixels = pixels; - this.Offset = offset; - } - - /// - /// Initializes a new instance of the struct from existing buffer. - /// will be set to of and will be set to 0. - /// - /// The pixel buffer - public JpegPixelArea(Buffer2D pixels) - : this(pixels, pixels.Width, 0) - { - } - - /// - /// Gets the pixels buffer. - /// - public Buffer2D Pixels { get; private set; } - - /// - /// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea)) - /// - public bool IsInitialized => this.Pixels != null; - - /// - /// Gets the stride. - /// - public int Stride { get; } - - /// - /// Gets the offset. - /// - public int Offset { get; } - - /// - /// Gets a of bytes to the pixel area - /// - public MutableSpan Span => new MutableSpan(this.Pixels.Array, this.Offset); - - /// - /// Returns the pixel at (x, y) - /// - /// The x index - /// The y index - /// The pixel value - public byte this[int x, int y] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.Pixels[(y * this.Stride) + x]; - } - } - - /// - /// Gets the subarea that belongs to the Block8x8 defined by block indices - /// - /// The block X index - /// The block Y index - /// The subarea offseted by block indices - public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by) - { - int offset = this.Offset + (8 * ((by * this.Stride) + bx)); - return new JpegPixelArea(this.Pixels, this.Stride, offset); - } - - /// - /// Gets the row offset at the given position - /// - /// The y-coordinate of the image. - /// The - public int GetRowOffset(int y) - { - return this.Offset + (y * this.Stride); - } - - /// - /// Load values to the pixel area from the given . - /// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to - /// values - /// - /// The block holding the color values - /// Temporal block provided by the caller - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp) - { - // Level shift by +128, clip to [0, 255], and write to dst. - block->CopyColorsTo(new MutableSpan(this.Pixels.Array, this.Offset), this.Stride, temp); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs deleted file mode 100644 index c9cc327b86..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - - using ImageSharp.Memory; - using SixLabors.Primitives; - - /// - /// Represents an image made up of three color components (luminance, blue chroma, red chroma) - /// - internal class YCbCrImage : IDisposable - { - // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P -#pragma warning disable SA1401 // FieldsMustBePrivate - /// - /// Gets the luminance components channel as . - /// - public Buffer2D YChannel; - - /// - /// Gets the blue chroma components channel as . - /// - public Buffer2D CbChannel; - - /// - /// Gets an offseted to the Cr channel - /// - public Buffer2D CrChannel; -#pragma warning restore SA1401 - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The height. - /// The ratio. - public YCbCrImage(int width, int height, YCbCrSubsampleRatio ratio) - { - Size cSize = CalculateChrominanceSize(width, height, ratio); - - this.Ratio = ratio; - this.YStride = width; - this.CStride = cSize.Width; - - this.YChannel = Buffer2D.CreateClean(width, height); - this.CbChannel = Buffer2D.CreateClean(cSize.Width, cSize.Height); - this.CrChannel = Buffer2D.CreateClean(cSize.Width, cSize.Height); - } - - /// - /// Provides enumeration of the various available subsample ratios. - /// - public enum YCbCrSubsampleRatio - { - /// - /// YCbCrSubsampleRatio444 - /// - YCbCrSubsampleRatio444, - - /// - /// YCbCrSubsampleRatio422 - /// - YCbCrSubsampleRatio422, - - /// - /// YCbCrSubsampleRatio420 - /// - YCbCrSubsampleRatio420, - - /// - /// YCbCrSubsampleRatio440 - /// - YCbCrSubsampleRatio440, - - /// - /// YCbCrSubsampleRatio411 - /// - YCbCrSubsampleRatio411, - - /// - /// YCbCrSubsampleRatio410 - /// - YCbCrSubsampleRatio410, - } - - /// - /// Gets the Y slice index delta between vertically adjacent pixels. - /// - public int YStride { get; } - - /// - /// Gets the red and blue chroma slice index delta between vertically adjacent pixels - /// that map to separate chroma samples. - /// - public int CStride { get; } - - /// - /// Gets or sets the subsampling ratio. - /// - public YCbCrSubsampleRatio Ratio { get; set; } - - /// - /// Disposes the returning rented arrays to the pools. - /// - public void Dispose() - { - this.YChannel.Dispose(); - this.CbChannel.Dispose(); - this.CrChannel.Dispose(); - } - - /// - /// Returns the offset of the first chroma component at the given row - /// - /// The row number. - /// - /// The . - /// - public int GetRowCOffset(int y) - { - switch (this.Ratio) - { - case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: - return y * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: - return (y / 2) * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: - return (y / 2) * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: - return y * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: - return (y / 2) * this.CStride; - default: - return y * this.CStride; - } - } - - /// - /// Returns the offset of the first luminance component at the given row - /// - /// The row number. - /// - /// The . - /// - public int GetRowYOffset(int y) - { - return y * this.YStride; - } - - /// - /// Returns the height and width of the chroma components - /// - /// The width. - /// The height. - /// The subsampling ratio. - /// The of the chrominance channel - internal static Size CalculateChrominanceSize( - int width, - int height, - YCbCrSubsampleRatio ratio) - { - switch (ratio) - { - case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: - return new Size((width + 1) / 2, height); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: - return new Size((width + 1) / 2, (height + 1) / 2); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: - return new Size(width, (height + 1) / 2); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: - return new Size((width + 3) / 4, height); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: - return new Size((width + 3) / 4, (height + 1) / 2); - default: - // Default to 4:4:4 subsampling. - return new Size(width, height); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/BlockQuad.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs similarity index 53% rename from src/ImageSharp/Formats/Jpeg/Components/BlockQuad.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs index 63453da21a..6b16ea824e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/BlockQuad.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs @@ -1,18 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg.Components + +using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components { /// - /// Poor man's stackalloc: Contains a value-type buffer sized for 4 instances. + /// Poor man's stackalloc: Contains a value-type buffer sized for 4 instances. /// Useful for decoder/encoder operations allocating a block for each Jpeg component. /// internal unsafe struct BlockQuad { /// - /// The value-type buffer sized for 4 instances. + /// The value-type buffer sized for 4 instances. /// - public fixed float Data[4 * Block8x8F.ScalarCount]; + public fixed float Data[4 * Block8x8F.Size]; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bits.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs similarity index 69% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/Bits.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs index 02f585be02..05bde78e65 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bits.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Holds the unprocessed bits that have been taken from the byte-stream. /// The n least significant bits of a form the unread bits, to be read in MSB to @@ -40,7 +38,7 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EnsureNBits(int n, ref InputProcessor inputProcessor) { - DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); + OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); errorCode.EnsureNoError(); } @@ -48,17 +46,17 @@ namespace ImageSharp.Formats.Jpg /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at /// least n. For best performance (avoiding function calls inside hot loops), /// the caller is the one responsible for first checking that bits.UnreadBits < n. - /// This method does not throw. Returns instead. + /// This method does not throw. Returns instead. /// /// The number of bits to ensure. /// The /// Error code - public DecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) + public OrigDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) { while (true) { - DecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); - if (errorCode != DecoderErrorCode.NoError || this.UnreadBits >= n) + OrigDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); + if (errorCode != OrigDecoderErrorCode.NoError || this.UnreadBits >= n) { return errorCode; } @@ -69,8 +67,8 @@ namespace ImageSharp.Formats.Jpg /// Unrolled version of for n==8 /// /// The - /// A - public DecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) + /// A + public OrigDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) { return this.EnsureBitsStepImpl(ref inputProcessor); } @@ -79,8 +77,8 @@ namespace ImageSharp.Formats.Jpg /// Unrolled version of for n==1 /// /// The - /// A - public DecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) + /// A + public OrigDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) { return this.EnsureBitsStepImpl(ref inputProcessor); } @@ -94,8 +92,7 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public int ReceiveExtend(int t, ref InputProcessor inputProcessor) { - int x; - DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out x); + OrigDecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out int x); errorCode.EnsureNoError(); return x; } @@ -106,13 +103,13 @@ namespace ImageSharp.Formats.Jpg /// Byte /// The /// Read bits value - /// The - public DecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) + /// The + public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) { if (this.UnreadBits < t) { - DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); - if (errorCode != DecoderErrorCode.NoError) + OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); + if (errorCode != OrigDecoderErrorCode.NoError) { x = int.MaxValue; return errorCode; @@ -122,22 +119,21 @@ namespace ImageSharp.Formats.Jpg this.UnreadBits -= t; this.Mask >>= t; int s = 1 << t; - x = (int)((this.Accumulator >> this.UnreadBits) & (s - 1)); + x = (this.Accumulator >> this.UnreadBits) & (s - 1); if (x < (s >> 1)) { x += ((-1) << t) + 1; } - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } - private DecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) + private OrigDecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) { - int c; - DecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out c); + OrigDecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out int c); - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { return errorCode; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs similarity index 74% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/Bytes.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 0e57e98d89..38507d58c8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - using System.IO; - using System.Runtime.CompilerServices; +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Bytes is a byte buffer, similar to a stream, except that it /// has to be able to unread more than 1 byte, due to byte stuffing. @@ -60,10 +59,10 @@ namespace ImageSharp.Formats.Jpg public static Bytes Create() { return new Bytes - { - Buffer = BytePool.Rent(BufferSize), - BufferAsInt = IntPool.Rent(BufferSize) - }; + { + Buffer = BytePool.Rent(BufferSize), + BufferAsInt = IntPool.Rent(BufferSize) + }; } /// @@ -86,8 +85,8 @@ namespace ImageSharp.Formats.Jpg /// /// Input stream /// The result byte as - /// The - public DecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) + /// The + public OrigDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) { // Take the fast path if bytes.buf contains at least two bytes. if (this.I + 2 <= this.J) @@ -95,50 +94,50 @@ namespace ImageSharp.Formats.Jpg x = this.BufferAsInt[this.I]; this.I++; this.UnreadableBytes = 1; - if (x != JpegConstants.Markers.XFFInt) + if (x != OrigJpegConstants.Markers.XFFInt) { - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } if (this.BufferAsInt[this.I] != 0x00) { - return DecoderErrorCode.MissingFF00; + return OrigDecoderErrorCode.MissingFF00; } this.I++; this.UnreadableBytes = 2; - x = JpegConstants.Markers.XFF; - return DecoderErrorCode.NoError; + x = OrigJpegConstants.Markers.XFF; + return OrigDecoderErrorCode.NoError; } this.UnreadableBytes = 0; - DecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); + OrigDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); this.UnreadableBytes = 1; - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { return errorCode; } - if (x != JpegConstants.Markers.XFF) + if (x != OrigJpegConstants.Markers.XFF) { - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); this.UnreadableBytes = 2; - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { return errorCode; } if (x != 0x00) { - return DecoderErrorCode.MissingFF00; + return OrigDecoderErrorCode.MissingFF00; } - x = JpegConstants.Markers.XFF; - return DecoderErrorCode.NoError; + x = OrigJpegConstants.Markers.XFF; + return OrigDecoderErrorCode.NoError; } /// @@ -149,26 +148,25 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte ReadByte(Stream inputStream) { - byte result; - DecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out result); + OrigDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result); errorCode.EnsureNoError(); return result; } /// /// Extracts the next byte, whether buffered or not buffered into the result out parameter. It does not care about byte stuffing. - /// This method does not throw on format error, it returns a instead. + /// This method does not throw on format error, it returns a instead. /// /// Input stream /// The result as out parameter - /// The - public DecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) + /// The + public OrigDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) { - DecoderErrorCode errorCode = DecoderErrorCode.NoError; + OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError; while (this.I == this.J) { errorCode = this.FillUnsafe(inputStream); - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { result = 0; return errorCode; @@ -186,15 +184,15 @@ namespace ImageSharp.Formats.Jpg /// /// The input stream /// The result - /// A + /// A [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) + public OrigDecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) { - DecoderErrorCode errorCode = DecoderErrorCode.NoError; + OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError; while (this.I == this.J) { errorCode = this.FillUnsafe(inputStream); - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { result = 0; return errorCode; @@ -216,18 +214,18 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Fill(Stream inputStream) { - DecoderErrorCode errorCode = this.FillUnsafe(inputStream); + OrigDecoderErrorCode errorCode = this.FillUnsafe(inputStream); errorCode.EnsureNoError(); } /// /// Fills up the bytes buffer from the underlying stream. /// It should only be called when there are no unread bytes in bytes. - /// This method does not throw , returns a instead! + /// This method does not throw , returns a instead! /// /// Input stream - /// The - public DecoderErrorCode FillUnsafe(Stream inputStream) + /// The + public OrigDecoderErrorCode FillUnsafe(Stream inputStream) { if (this.I != this.J) { @@ -249,7 +247,7 @@ namespace ImageSharp.Formats.Jpg int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); if (n == 0) { - return DecoderErrorCode.UnexpectedEndOfStream; + return OrigDecoderErrorCode.UnexpectedEndOfStream; } this.J += n; @@ -259,7 +257,7 @@ namespace ImageSharp.Formats.Jpg this.BufferAsInt[i] = this.Buffer[i]; } - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs similarity index 65% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderThrowHelper.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs index 9ce5ea4146..d5a9340d72 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs @@ -1,32 +1,30 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Encapsulates exception thrower methods for the Jpeg Encoder /// internal static class DecoderThrowHelper { /// - /// Throws an exception that belongs to the given + /// Throws an exception that belongs to the given /// - /// The + /// The [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowExceptionForErrorCode(this DecoderErrorCode errorCode) + public static void ThrowExceptionForErrorCode(this OrigDecoderErrorCode errorCode) { switch (errorCode) { - case DecoderErrorCode.NoError: + case OrigDecoderErrorCode.NoError: throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); - case DecoderErrorCode.MissingFF00: + case OrigDecoderErrorCode.MissingFF00: throw new MissingFF00Exception(); - case DecoderErrorCode.UnexpectedEndOfStream: + case OrigDecoderErrorCode.UnexpectedEndOfStream: throw new EOFException(); default: throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); @@ -34,26 +32,26 @@ namespace ImageSharp.Formats.Jpg } /// - /// Throws an exception if the given defines an error. + /// Throws an exception if the given defines an error. /// - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoError(this DecoderErrorCode errorCode) + public static void EnsureNoError(this OrigDecoderErrorCode errorCode) { - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { ThrowExceptionForErrorCode(errorCode); } } /// - /// Throws an exception if the given is . + /// Throws an exception if the given is . /// - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoEOF(this DecoderErrorCode errorCode) + public static void EnsureNoEOF(this OrigDecoderErrorCode errorCode) { - if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + if (errorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) { errorCode.ThrowExceptionForErrorCode(); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/EOFException.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs similarity index 75% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/EOFException.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs index 5ed25ef049..60d9b1e1af 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/EOFException.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// The EOF (End of File exception). /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs similarity index 66% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/InputProcessor.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index 60042d36f8..a7a5fcd986 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; +using System; +using System.IO; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// - /// Encapsulates stream reading and processing data and operations for . + /// Encapsulates stream reading and processing data and operations for . /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s /// internal struct InputProcessor : IDisposable @@ -29,14 +27,14 @@ namespace ImageSharp.Formats.Jpg /// Initializes a new instance of the struct. /// /// The input - /// Temporal buffer, same as + /// Temporal buffer, same as public InputProcessor(Stream inputStream, byte[] temp) { this.Bits = default(Bits); this.Bytes = Bytes.Create(); this.InputStream = inputStream; this.Temp = temp; - this.UnexpectedEndOfStreamReached = false; + this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// @@ -45,53 +43,53 @@ namespace ImageSharp.Formats.Jpg public Stream InputStream { get; } /// - /// Gets the temporal buffer, same instance as + /// Gets the temporal buffer, same instance as /// public byte[] Temp { get; } /// - /// Gets or sets a value indicating whether an unexpected EOF reached in . + /// Gets a value indicating whether an unexpected EOF reached in . /// - public bool UnexpectedEndOfStreamReached { get; set; } + public bool ReachedEOF => this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream; + + public bool HasError => this.LastErrorCode != OrigDecoderErrorCode.NoError; + + public OrigDecoderErrorCode LastErrorCode { get; private set; } + + public void ResetErrorState() => this.LastErrorCode = OrigDecoderErrorCode.NoError; /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. + /// If errorCode indicates unexpected EOF, sets to true and returns false. /// Calls and returns true otherwise. /// - /// The - /// indicating whether everything is OK - public bool CheckEOFEnsureNoError(DecoderErrorCode errorCode) + /// A indicating whether EOF reached + public bool CheckEOFEnsureNoError() { - if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) { - this.UnexpectedEndOfStreamReached = true; return false; } - errorCode.EnsureNoError(); + this.LastErrorCode.EnsureNoError(); return true; } /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. + /// If errorCode indicates unexpected EOF, sets to true and returns false. /// Returns true otherwise. /// - /// The - /// indicating whether everything is OK - public bool CheckEOF(DecoderErrorCode errorCode) + /// A indicating whether EOF reached + public bool CheckEOF() { - if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) { - this.UnexpectedEndOfStreamReached = true; return false; } return true; } - /// - /// Dispose - /// + /// public void Dispose() { this.Bytes.Dispose(); @@ -112,34 +110,34 @@ namespace ImageSharp.Formats.Jpg /// TODO: This method (and also the usages) could be optimized by batching! /// /// The decoded bit as a - /// The - public DecoderErrorCode DecodeBitUnsafe(out bool result) + /// The + public OrigDecoderErrorCode DecodeBitUnsafe(out bool result) { if (this.Bits.UnreadBits == 0) { - DecoderErrorCode errorCode = this.Bits.Ensure1BitUnsafe(ref this); - if (errorCode != DecoderErrorCode.NoError) + this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this); + if (this.LastErrorCode != OrigDecoderErrorCode.NoError) { result = false; - return errorCode; + return this.LastErrorCode; } } result = (this.Bits.Accumulator & this.Bits.Mask) != 0; this.Bits.UnreadBits--; this.Bits.Mask >>= 1; - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// /// Reads exactly length bytes into data. It does not care about byte stuffing. - /// Does not throw on errors, returns instead! + /// Does not throw on errors, returns instead! /// /// The data to write to. /// The offset in the source buffer /// The number of bytes to read - /// The - public DecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) + /// The + public OrigDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) { // Unread the overshot bytes, if any. if (this.Bytes.UnreadableBytes != 0) @@ -152,8 +150,8 @@ namespace ImageSharp.Formats.Jpg this.Bytes.UnreadableBytes = 0; } - DecoderErrorCode errorCode = DecoderErrorCode.NoError; - while (length > 0) + this.LastErrorCode = OrigDecoderErrorCode.NoError; + while (length > 0 && this.LastErrorCode == OrigDecoderErrorCode.NoError) { if (this.Bytes.J - this.Bytes.I >= length) { @@ -168,11 +166,11 @@ namespace ImageSharp.Formats.Jpg length -= this.Bytes.J - this.Bytes.I; this.Bytes.I += this.Bytes.J - this.Bytes.I; - errorCode = this.Bytes.FillUnsafe(this.InputStream); + this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); } } - return errorCode; + return this.LastErrorCode; } /// @@ -180,19 +178,24 @@ namespace ImageSharp.Formats.Jpg /// /// The number of bits to decode. /// The result - /// The - public DecoderErrorCode DecodeBitsUnsafe(int count, out int result) + /// The + public OrigDecoderErrorCode DecodeBitsUnsafe(int count, out int result) { if (this.Bits.UnreadBits < count) { - this.Bits.EnsureNBits(count, ref this); + this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this); + if (this.LastErrorCode != OrigDecoderErrorCode.NoError) + { + result = 0; + return this.LastErrorCode; + } } result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); result = result & ((1 << count) - 1); this.Bits.UnreadBits -= count; this.Bits.Mask >>= count; - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// @@ -200,8 +203,8 @@ namespace ImageSharp.Formats.Jpg /// /// The huffman value /// The decoded - /// The - public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out int result) + /// The + public OrigDecoderErrorCode DecodeHuffmanUnsafe(ref OrigHuffmanTree huffmanTree, out int result) { result = 0; @@ -212,11 +215,11 @@ namespace ImageSharp.Formats.Jpg if (this.Bits.UnreadBits < 8) { - DecoderErrorCode errorCode = this.Bits.Ensure8BitsUnsafe(ref this); + this.LastErrorCode = this.Bits.Ensure8BitsUnsafe(ref this); - if (errorCode == DecoderErrorCode.NoError) + if (this.LastErrorCode == OrigDecoderErrorCode.NoError) { - int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSizeLog2)) & 0xFF; + int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - OrigHuffmanTree.LutSizeLog2)) & 0xFF; int v = huffmanTree.Lut[lutIndex]; if (v != 0) @@ -225,22 +228,27 @@ namespace ImageSharp.Formats.Jpg this.Bits.UnreadBits -= n; this.Bits.Mask >>= n; result = v >> 8; - return errorCode; + return this.LastErrorCode; } } else { this.UnreadByteStuffedByte(); - return errorCode; + return this.LastErrorCode; } } int code = 0; - for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) + for (int i = 0; i < OrigHuffmanTree.MaxCodeLength; i++) { if (this.Bits.UnreadBits == 0) { - this.Bits.EnsureNBits(1, ref this); + this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(1, ref this); + + if (this.HasError) + { + return this.LastErrorCode; + } } if ((this.Bits.Accumulator & this.Bits.Mask) != 0) @@ -254,7 +262,7 @@ namespace ImageSharp.Formats.Jpg if (code <= huffmanTree.MaxCodes[i]) { result = huffmanTree.GetValue(code, i); - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } code <<= 1; @@ -264,7 +272,7 @@ namespace ImageSharp.Formats.Jpg DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode(); // DUMMY RETURN! C# doesn't know we have thrown an exception! - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } /// @@ -274,17 +282,17 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Skip(int count) { - DecoderErrorCode errorCode = this.SkipUnsafe(count); - errorCode.EnsureNoError(); + this.LastErrorCode = this.SkipUnsafe(count); + this.LastErrorCode.EnsureNoError(); } /// /// Skips the next n bytes. - /// Does not throw, returns instead! + /// Does not throw, returns instead! /// /// The number of bytes to ignore. - /// The - public DecoderErrorCode SkipUnsafe(int count) + /// The + public OrigDecoderErrorCode SkipUnsafe(int count) { // Unread the overshot bytes, if any. if (this.Bytes.UnreadableBytes != 0) @@ -312,14 +320,14 @@ namespace ImageSharp.Formats.Jpg break; } - DecoderErrorCode errorCode = this.Bytes.FillUnsafe(this.InputStream); - if (errorCode != DecoderErrorCode.NoError) + this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); + if (this.LastErrorCode != OrigDecoderErrorCode.NoError) { - return errorCode; + return this.LastErrorCode; } } - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// @@ -331,8 +339,8 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadFull(byte[] data, int offset, int length) { - DecoderErrorCode errorCode = this.ReadFullUnsafe(data, offset, length); - errorCode.EnsureNoError(); + this.LastErrorCode = this.ReadFullUnsafe(data, offset, length); + this.LastErrorCode.EnsureNoError(); } /// @@ -359,10 +367,11 @@ namespace ImageSharp.Formats.Jpg /// /// Byte /// Read bits value - /// The - public DecoderErrorCode ReceiveExtendUnsafe(int t, out int x) + /// The + public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, out int x) { - return this.Bits.ReceiveExtendUnsafe(t, ref this, out x); + this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); + return this.LastErrorCode; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.md b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md similarity index 100% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.md rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/MissingFF00Exception.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs similarity index 54% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/MissingFF00Exception.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs index f8c157237d..005034b9dc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/MissingFF00Exception.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// The missing ff00 exception. /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs new file mode 100644 index 0000000000..c87752b371 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs @@ -0,0 +1,244 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ + /// + /// + /// Represents a single color component + /// + internal class OrigComponent : IDisposable, IJpegComponent + { + public OrigComponent(byte identifier, int index) + { + this.Identifier = identifier; + this.Index = index; + } + + /// + /// Gets the identifier + /// + public byte Identifier { get; } + + /// + public int Index { get; } + + public Size SizeInBlocks { get; private set; } + + public Size SamplingFactors { get; private set; } + + public Size SubSamplingDivisors { get; private set; } + + public int HorizontalSamplingFactor => this.SamplingFactors.Width; + + public int VerticalSamplingFactor => this.SamplingFactors.Height; + + /// + public int QuantizationTableIndex { get; private set; } + + /// + /// + /// Gets the storing the "raw" frequency-domain decoded blocks. + /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. + /// This is done by . + /// When us true, we are touching these blocks multiple times - each time we process a Scan. + /// + public Buffer2D SpectralBlocks { get; private set; } + + /// + /// Initializes + /// + /// The instance + public void InitializeDerivedData(OrigJpegDecoderCore decoder) + { + // For 4-component images (either CMYK or YCbCrK), we only support two + // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. + // Theoretically, 4-component JPEG images could mix and match hv values + // but in practice, those two combinations are the only ones in use, + // and it simplifies the applyBlack code below if we can assume that: + // - for CMYK, the C and K channels have full samples, and if the M + // and Y channels subsample, they subsample both horizontally and + // vertically. + // - for YCbCrK, the Y and K channels have full samples. + this.SizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(this.SamplingFactors); + + if (this.Index == 0 || this.Index == 3) + { + this.SubSamplingDivisors = new Size(1, 1); + } + else + { + OrigComponent c0 = decoder.Components[0]; + this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); + } + + this.SpectralBlocks = Buffer2D.CreateClean(this.SizeInBlocks); + } + + /// + /// Initializes all component data except . + /// + /// The instance + public void InitializeCoreData(OrigJpegDecoderCore decoder) + { + // Section B.2.2 states that "the value of C_i shall be different from + // the values of C_1 through C_(i-1)". + int i = this.Index; + + for (int j = 0; j < this.Index; j++) + { + if (this.Identifier == decoder.Components[j].Identifier) + { + throw new ImageFormatException("Repeated component identifier"); + } + } + + this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)]; + if (this.QuantizationTableIndex > OrigJpegDecoderCore.MaxTq) + { + throw new ImageFormatException("Bad Tq value"); + } + + byte hv = decoder.Temp[7 + (3 * i)]; + int h = hv >> 4; + int v = hv & 0x0f; + if (h < 1 || h > 4 || v < 1 || v > 4) + { + throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio"); + } + + if (h == 3 || v == 3) + { + throw new ImageFormatException("Lnsupported subsampling ratio"); + } + + switch (decoder.ComponentCount) + { + case 1: + + // If a JPEG image has only one component, section A.2 says "this data + // is non-interleaved by definition" and section A.2.2 says "[in this + // case...] the order of data units within a scan shall be left-to-right + // and top-to-bottom... regardless of the values of H_1 and V_1". Section + // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be + // one data unit". Similarly, section A.1.1 explains that it is the ratio + // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale + // images, H_1 is the maximum H_j for all components j, so that ratio is + // always 1. The component's (h, v) is effectively always (1, 1): even if + // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 + // MCUs, not two 16x8 MCUs. + h = 1; + v = 1; + break; + + case 3: + + // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, + // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the + // (h, v) values for the Y component are either (1, 1), (1, 2), + // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values + // must be a multiple of the Cb and Cr component's values. We also + // assume that the two chroma components have the same subsampling + // ratio. + switch (i) + { + case 0: + { + // Y. + // We have already verified, above, that h and v are both + // either 1, 2 or 4, so invalid (h, v) combinations are those + // with v == 4. + if (v == 4) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + case 1: + { + // Cb. + Size s0 = decoder.Components[0].SamplingFactors; + + if (s0.Width % h != 0 || s0.Height % v != 0) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + case 2: + { + // Cr. + Size s1 = decoder.Components[1].SamplingFactors; + + if (s1.Width != h || s1.Height != v) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + } + + break; + + case 4: + + // For 4-component images (either CMYK or YCbCrK), we only support two + // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. + // Theoretically, 4-component JPEG images could mix and match hv values + // but in practice, those two combinations are the only ones in use, + // and it simplifies the applyBlack code below if we can assume that: + // - for CMYK, the C and K channels have full samples, and if the M + // and Y channels subsample, they subsample both horizontally and + // vertically. + // - for YCbCrK, the Y and K channels have full samples. + switch (i) + { + case 0: + if (hv != 0x11 && hv != 0x22) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + case 1: + case 2: + if (hv != 0x11) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + case 3: + Size s0 = decoder.Components[0].SamplingFactors; + + if (s0.Width != h || s0.Height != v) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + break; + } + + this.SamplingFactors = new Size(h, v); + } + + public void Dispose() + { + this.SpectralBlocks.Dispose(); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentScan.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs similarity index 69% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentScan.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs index 89fc115ea3..0d98044045 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentScan.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Represents a component scan /// [StructLayout(LayoutKind.Sequential)] - internal struct ComponentScan + internal struct OrigComponentScan { /// /// Gets or sets the component index. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderErrorCode.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs similarity index 70% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderErrorCode.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs index 8b82184faf..02a8ea55e0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderErrorCode.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// /// Represents "recoverable" decoder errors. /// - internal enum DecoderErrorCode + internal enum OrigDecoderErrorCode { /// /// NoError diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTree.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs index 390e5dd150..4c97d57415 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; +using System; +using System.Buffers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Represents a Huffman tree /// - internal struct HuffmanTree : IDisposable + internal struct OrigHuffmanTree : IDisposable { /// /// The index of the AC table row @@ -99,12 +98,12 @@ namespace ImageSharp.Formats.Jpg private static readonly ArrayPool CodesPool16 = ArrayPool.Create(MaxCodeLength, 50); /// - /// Creates and initializes an array of instances of size + /// Creates and initializes an array of instances of size /// - /// An array of instances representing the Huffman tables - public static HuffmanTree[] CreateHuffmanTrees() + /// An array of instances representing the Huffman tables + public static OrigHuffmanTree[] CreateHuffmanTrees() { - HuffmanTree[] result = new HuffmanTree[NumberOfTrees]; + OrigHuffmanTree[] result = new OrigHuffmanTree[NumberOfTrees]; for (int i = 0; i < MaxTc + 1; i++) { for (int j = 0; j < MaxTh + 1; j++) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs similarity index 59% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs index 7b910cdd24..c9bb898aa5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Conains the definition of /// - internal unsafe partial struct JpegScanDecoder + internal unsafe partial struct OrigJpegScanDecoder { /// /// Holds the "large" data blocks needed for computations. @@ -21,22 +20,22 @@ namespace ImageSharp.Formats.Jpg /// /// The main input/working block /// - public Block8x8F Block; + public Block8x8 Block; /// /// The jpeg unzig data /// - public UnzigData Unzig; + public ZigZag Unzig; /// - /// The buffer storing the -s for each component + /// The buffer storing the -s for each component /// - public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents]; + public fixed byte ScanData[3 * OrigJpegDecoderCore.MaxComponents]; /// /// The DC values for each component /// - public fixed int Dc[JpegDecoderCore.MaxComponents]; + public fixed int Dc[OrigJpegDecoderCore.MaxComponents]; /// /// Creates and initializes a new instance @@ -44,8 +43,8 @@ namespace ImageSharp.Formats.Jpg /// The public static ComputationData Create() { - ComputationData data = default(ComputationData); - data.Unzig = UnzigData.Create(); + var data = default(ComputationData); + data.Unzig = ZigZag.CreateUnzigTable(); return data; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs similarity index 78% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs index 52e25f3a81..0098b4a4ed 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs @@ -1,14 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg +using SixLabors.ImageSharp.Formats.Jpeg.Common; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// /// Conains the definition of /// - internal unsafe partial struct JpegScanDecoder + internal unsafe partial struct OrigJpegScanDecoder { /// /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of @@ -18,7 +18,7 @@ namespace ImageSharp.Formats.Jpg /// /// Pointer to /// - public Block8x8F* Block; + public Block8x8* Block; /// /// Pointer to as int* @@ -28,7 +28,7 @@ namespace ImageSharp.Formats.Jpg /// /// Pointer to as Scan* /// - public ComponentScan* ComponentScan; + public OrigComponentScan* ComponentScan; /// /// Pointer to @@ -43,7 +43,7 @@ namespace ImageSharp.Formats.Jpg { this.Block = &basePtr->Block; this.Unzig = basePtr->Unzig.Data; - this.ComponentScan = (ComponentScan*)basePtr->ScanData; + this.ComponentScan = (OrigComponentScan*)basePtr->ScanData; this.Dc = basePtr->Dc; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs similarity index 72% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 7d2e6d4414..67abba9f33 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.Memory; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md! /// @@ -30,7 +29,7 @@ namespace ImageSharp.Formats.Jpg /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. /// [StructLayout(LayoutKind.Sequential)] - internal unsafe partial struct JpegScanDecoder + internal unsafe partial struct OrigJpegScanDecoder { // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. #pragma warning disable SA1202 // ElementsMustBeOrderedByAccess @@ -96,12 +95,12 @@ namespace ImageSharp.Formats.Jpg private int eobRun; /// - /// Initializes a default-constructed instance for reading data from -s stream. + /// Initializes a default-constructed instance for reading data from -s stream. /// - /// Pointer to on the stack - /// The instance + /// Pointer to on the stack + /// The instance /// The remaining bytes in the segment block. - public static void InitStreamReading(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) + public static void InitStreamReading(OrigJpegScanDecoder* p, OrigJpegDecoderCore decoder, int remaining) { p->data = ComputationData.Create(); p->pointers = new DataPointers(&p->data); @@ -109,8 +108,8 @@ namespace ImageSharp.Formats.Jpg } /// - /// Read Huffman data from Jpeg scans in , - /// and decode it as into . + /// Read Huffman data from Jpeg scans in , + /// and decode it as into . /// /// The blocks are traversed one MCU at a time. For 4:2:0 chroma /// subsampling, there are four Y 8x8 blocks in every 16x16 MCU. @@ -135,12 +134,14 @@ namespace ImageSharp.Formats.Jpg /// 0 1 2 /// 3 4 5 /// - /// The instance - public void DecodeBlocks(JpegDecoderCore decoder) + /// The instance + public void DecodeBlocks(OrigJpegDecoderCore decoder) { + decoder.InputProcessor.ResetErrorState(); + int blockCount = 0; int mcu = 0; - byte expectedRst = JpegConstants.Markers.RST0; + byte expectedRst = OrigJpegConstants.Markers.RST0; for (int my = 0; my < decoder.MCUCountY; my++) { @@ -149,8 +150,10 @@ namespace ImageSharp.Formats.Jpg for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) { this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - this.hi = decoder.ComponentArray[this.ComponentIndex].HorizontalFactor; - int vi = decoder.ComponentArray[this.ComponentIndex].VerticalFactor; + OrigComponent component = decoder.Components[this.ComponentIndex]; + + this.hi = component.HorizontalSamplingFactor; + int vi = component.VerticalSamplingFactor; for (int j = 0; j < this.hi * vi; j++) { @@ -171,18 +174,19 @@ namespace ImageSharp.Formats.Jpg } } - // Take an existing block (required when progressive): - int blockIndex = this.GetBlockIndex(decoder); - this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block; + // Find the block at (bx,by) in the component's buffer: + ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); - if (!decoder.InputProcessor.UnexpectedEndOfStreamReached) + // Copy block to stack + this.data.Block = blockRefOnHeap; + + if (!decoder.InputProcessor.ReachedEOF) { this.DecodeBlock(decoder, scanIndex); } - // Store the decoded block - Buffer blocks = decoder.DecodedBlocks[this.ComponentIndex]; - blocks[blockIndex].SaveBlock(this.bx, this.by, ref this.data.Block); + // Store the result block: + blockRefOnHeap = this.data.Block; } // for j @@ -195,10 +199,10 @@ namespace ImageSharp.Formats.Jpg { // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, // but this one assumes well-formed input, and hence the restart marker follows immediately. - if (!decoder.InputProcessor.UnexpectedEndOfStreamReached) + if (!decoder.InputProcessor.ReachedEOF) { - DecoderErrorCode errorCode = decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); - if (decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); + if (decoder.InputProcessor.CheckEOFEnsureNoError()) { if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) { @@ -206,9 +210,9 @@ namespace ImageSharp.Formats.Jpg } expectedRst++; - if (expectedRst == JpegConstants.Markers.RST7 + 1) + if (expectedRst == OrigJpegConstants.Markers.RST7 + 1) { - expectedRst = JpegConstants.Markers.RST0; + expectedRst = OrigJpegConstants.Markers.RST0; } } } @@ -230,15 +234,15 @@ namespace ImageSharp.Formats.Jpg private void ResetDc() { - Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); + Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents); } /// /// The implementation part of as an instance method. /// - /// The + /// The /// The remaining bytes - private void InitStreamReadingImpl(JpegDecoderCore decoder, int remaining) + private void InitStreamReadingImpl(OrigJpegDecoderCore decoder, int remaining) { if (decoder.ComponentCount == 0) { @@ -273,7 +277,7 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Total sampling factors too large."); } - this.zigEnd = Block8x8F.ScalarCount - 1; + this.zigEnd = Block8x8F.Size - 1; if (decoder.IsProgressive) { @@ -283,7 +287,7 @@ namespace ImageSharp.Formats.Jpg this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f; if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd - || this.zigEnd >= Block8x8F.ScalarCount) + || this.zigEnd >= Block8x8F.Size) { throw new ImageFormatException("Bad spectral selection bounds"); } @@ -305,10 +309,10 @@ namespace ImageSharp.Formats.Jpg /// /// The decoder /// The index of the scan - private void DecodeBlock(JpegDecoderCore decoder, int scanIndex) + private void DecodeBlock(OrigJpegDecoderCore decoder, int scanIndex) { - Block8x8F* b = this.pointers.Block; - int huffmannIdx = (HuffmanTree.AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; + Block8x8* b = this.pointers.Block; + int huffmannIdx = (OrigHuffmanTree.AcTableIndex * OrigHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); @@ -316,18 +320,17 @@ namespace ImageSharp.Formats.Jpg else { int zig = this.zigStart; - DecoderErrorCode errorCode; + if (zig == 0) { zig++; // Decode the DC coefficient, as specified in section F.2.2.1. - int value; - int huffmanIndex = (HuffmanTree.DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; - errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe( + int huffmanIndex = (OrigHuffmanTree.DcTableIndex * OrigHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; + decoder.InputProcessor.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], - out value); - if (!decoder.InputProcessor.CheckEOF(errorCode)) + out int value); + if (!decoder.InputProcessor.CheckEOF()) { return; } @@ -337,9 +340,8 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Excessive DC component"); } - int deltaDC; - errorCode = decoder.InputProcessor.ReceiveExtendUnsafe(value, out deltaDC); - if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + decoder.InputProcessor.ReceiveExtendUnsafe(value, out int deltaDC); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) { return; } @@ -347,7 +349,8 @@ namespace ImageSharp.Formats.Jpg this.pointers.Dc[this.ComponentIndex] += deltaDC; // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.ComponentIndex] << this.al); + value = this.pointers.Dc[this.ComponentIndex] << this.al; + Block8x8.SetScalarAt(b, 0, (short)value); } if (zig <= this.zigEnd && this.eobRun > 0) @@ -359,9 +362,8 @@ namespace ImageSharp.Formats.Jpg // Decode the AC coefficients, as specified in section F.2.2.2. for (; zig <= this.zigEnd; zig++) { - int value; - errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); - if (!decoder.InputProcessor.CheckEOF(errorCode)) + decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out int value); + if (decoder.InputProcessor.HasError) { return; } @@ -376,15 +378,15 @@ namespace ImageSharp.Formats.Jpg break; } - int ac; - errorCode = decoder.InputProcessor.ReceiveExtendUnsafe(val1, out ac); - if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + decoder.InputProcessor.ReceiveExtendUnsafe(val1, out int ac); + if (decoder.InputProcessor.HasError) { return; } // b[Unzig[zig]] = ac << al; - Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); + value = ac << this.al; + Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)value); } else { @@ -393,8 +395,8 @@ namespace ImageSharp.Formats.Jpg this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - errorCode = this.DecodeEobRun(val0, ref decoder.InputProcessor); - if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + this.DecodeEobRun(val0, ref decoder.InputProcessor); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) { return; } @@ -411,30 +413,18 @@ namespace ImageSharp.Formats.Jpg } } - private DecoderErrorCode DecodeEobRun(int count, ref InputProcessor decoder) + private void DecodeEobRun(int count, ref InputProcessor processor) { - int bitsResult; - DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); - if (errorCode != DecoderErrorCode.NoError) + processor.DecodeBitsUnsafe(count, out int bitsResult); + if (processor.LastErrorCode != OrigDecoderErrorCode.NoError) { - return errorCode; + return; } this.eobRun |= bitsResult; - return DecoderErrorCode.NoError; - } - - /// - /// Gets the block index used to retieve blocks from in - /// - /// The instance - /// The index - private int GetBlockIndex(JpegDecoderCore decoder) - { - return ((this.by * decoder.MCUCountX) * this.hi) + this.bx; } - private void InitComponentScan(JpegDecoderCore decoder, int i, ref ComponentScan currentComponentScan, ref int totalHv) + private void InitComponentScan(OrigJpegDecoderCore decoder, int i, ref OrigComponentScan currentComponentScan, ref int totalHv) { // Component selector. int cs = decoder.Temp[1 + (2 * i)]; @@ -442,7 +432,7 @@ namespace ImageSharp.Formats.Jpg for (int j = 0; j < decoder.ComponentCount; j++) { // Component compv = ; - if (cs == decoder.ComponentArray[j].Identifier) + if (cs == decoder.Components[j].Identifier) { compIndex = j; } @@ -455,15 +445,15 @@ namespace ImageSharp.Formats.Jpg currentComponentScan.ComponentIndex = (byte)compIndex; - this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); + this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, decoder.Components[compIndex]); } private void ProcessComponentImpl( - JpegDecoderCore decoder, + OrigJpegDecoderCore decoder, int i, - ref ComponentScan currentComponentScan, + ref OrigComponentScan currentComponentScan, ref int totalHv, - ref Component currentComponent) + OrigComponent currentComponent) { // Section B.2.3 states that "the value of Cs_j shall be different from // the values of Cs_1 through Cs_(j-1)". Since we have previously @@ -478,16 +468,16 @@ namespace ImageSharp.Formats.Jpg } } - totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; + totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor; currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); - if (currentComponentScan.DcTableSelector > HuffmanTree.MaxTh) + if (currentComponentScan.DcTableSelector > OrigHuffmanTree.MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); - if (currentComponentScan.AcTableSelector > HuffmanTree.MaxTh) + if (currentComponentScan.AcTableSelector > OrigHuffmanTree.MaxTh) { throw new ImageFormatException("Bad AC table selector value"); } @@ -499,9 +489,9 @@ namespace ImageSharp.Formats.Jpg /// The instance /// The Huffman tree /// The low transform offset - private void Refine(ref InputProcessor bp, ref HuffmanTree h, int delta) + private void Refine(ref InputProcessor bp, ref OrigHuffmanTree h, int delta) { - Block8x8F* b = this.pointers.Block; + Block8x8* b = this.pointers.Block; // Refining a DC component is trivial. if (this.zigStart == 0) @@ -511,22 +501,21 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Invalid state for zig DC component"); } - bool bit; - DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); - if (!bp.CheckEOFEnsureNoError(errorCode)) + bp.DecodeBitUnsafe(out bool bit); + if (!bp.CheckEOFEnsureNoError()) { return; } if (bit) { - int stuff = (int)Block8x8F.GetScalarAt(b, 0); + int stuff = (int)Block8x8.GetScalarAt(b, 0); // int stuff = (int)b[0]; stuff |= delta; // b[0] = stuff; - Block8x8F.SetScalarAt(b, 0, stuff); + Block8x8.SetScalarAt(b, 0, (short)stuff); } return; @@ -541,9 +530,8 @@ namespace ImageSharp.Formats.Jpg bool done = false; int z = 0; - int val; - DecoderErrorCode errorCode = bp.DecodeHuffmanUnsafe(ref h, out val); - if (!bp.CheckEOF(errorCode)) + bp.DecodeHuffmanUnsafe(ref h, out int val); + if (!bp.CheckEOF()) { return; } @@ -559,8 +547,8 @@ namespace ImageSharp.Formats.Jpg this.eobRun = 1 << val0; if (val0 != 0) { - errorCode = this.DecodeEobRun(val0, ref bp); - if (!bp.CheckEOFEnsureNoError(errorCode)) + this.DecodeEobRun(val0, ref bp); + if (!bp.CheckEOFEnsureNoError()) { return; } @@ -573,9 +561,8 @@ namespace ImageSharp.Formats.Jpg case 1: z = delta; - bool bit; - errorCode = bp.DecodeBitUnsafe(out bit); - if (!bp.CheckEOFEnsureNoError(errorCode)) + bp.DecodeBitUnsafe(out bool bit); + if (!bp.CheckEOFEnsureNoError()) { return; } @@ -596,20 +583,16 @@ namespace ImageSharp.Formats.Jpg } zig = this.RefineNonZeroes(ref bp, zig, val0, delta); - if (bp.UnexpectedEndOfStreamReached) - { - return; - } - if (zig > this.zigEnd) + if (bp.ReachedEOF || bp.HasError) { - throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); + return; } - if (z != 0) + if (z != 0 && zig <= this.zigEnd) { // b[Unzig[zig]] = z; - Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z); + Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)z); } } } @@ -632,11 +615,11 @@ namespace ImageSharp.Formats.Jpg /// The private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta) { - Block8x8F* b = this.pointers.Block; + Block8x8* b = this.pointers.Block; for (; zig <= this.zigEnd; zig++) { int u = this.pointers.Unzig[zig]; - float bu = Block8x8F.GetScalarAt(b, u); + int bu = Block8x8.GetScalarAt(b, u); // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? if (bu == 0) @@ -650,9 +633,8 @@ namespace ImageSharp.Formats.Jpg continue; } - bool bit; - DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); - if (!bp.CheckEOFEnsureNoError(errorCode)) + bp.DecodeBitUnsafe(out bool bit); + if (bp.HasError) { return int.MinValue; } @@ -662,16 +644,9 @@ namespace ImageSharp.Formats.Jpg continue; } - if (bu >= 0) - { - // b[u] += delta; - Block8x8F.SetScalarAt(b, u, bu + delta); - } - else - { - // b[u] -= delta; - Block8x8F.SetScalarAt(b, u, bu - delta); - } + int val = bu >= 0 ? bu + delta : bu - delta; + + Block8x8.SetScalarAt(b, u, (short)val); } return zig; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs similarity index 81% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs index 3875cc12fe..23fcda2964 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs @@ -1,8 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// Enumerates the Huffman tables diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs index d0003b919d..2fb01c5c8c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// A compiled look-up table representation of a huffmanSpec. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs similarity index 97% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs index a0eea6e718..8e40cb3689 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs @@ -1,8 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// The Huffman encoding specifications. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs similarity index 71% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs index 5a469e0e9d..459d29f91f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs @@ -1,8 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// Enumerates the quantization tables diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs similarity index 95% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs index 94173d3e43..02bd451b94 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder +{ /// /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Methods to build the tables are based on libjpeg implementation. diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index d2b7d2d7c4..2912a87199 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -1,19 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { - using System.Buffers; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; - using ImageSharp.Formats.Jpg.Components; - using ImageSharp.PixelFormats; - /// /// Image encoder for writing an image to a stream as a jpeg. /// @@ -57,7 +58,7 @@ namespace ImageSharp.Formats /// private static readonly byte[] SosHeaderYCbCr = { - JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, + OrigJpegConstants.Markers.XFF, OrigJpegConstants.Markers.SOS, // Marker 0x00, 0x0c, @@ -124,6 +125,21 @@ namespace ImageSharp.Formats /// private readonly byte[] huffmanBuffer = new byte[179]; + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + private readonly bool ignoreMetadata; + + /// + /// The quality, that will be used to encode the image. + /// + private readonly int quality; + + /// + /// Gets or sets the subsampling method to use. + /// + private readonly JpegSubsample? subsample; + /// /// The accumulated bits to write to the stream. /// @@ -149,37 +165,15 @@ namespace ImageSharp.Formats /// private Stream outputStream; - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - private bool ignoreMetadata = false; - - /// - /// Gets or sets the quality, that will be used to encode the image. Quality - /// index must be between 0 and 100 (compression from max to min). - /// - /// The quality of the jpg image from 0 to 100. - private int quality = 0; - - /// - /// Gets or sets the subsampling method to use. - /// - private JpegSubsample? subsample; - /// /// Initializes a new instance of the class. /// /// The options public JpegEncoderCore(IJpegEncoderOptions options) { - int quality = options.Quality; - if (quality == 0) - { - quality = 75; - } - - this.quality = quality; - this.subsample = options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); + // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1. + this.quality = options.Quality.Clamp(1, 100); + this.subsample = options.Subsample ?? (this.quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); this.ignoreMetadata = options.IgnoreMetadata; } @@ -196,7 +190,7 @@ namespace ImageSharp.Formats Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - ushort max = JpegConstants.MaxLength; + ushort max = OrigJpegConstants.MaxLength; if (image.Width >= max || image.Height >= max) { throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); @@ -204,17 +198,15 @@ namespace ImageSharp.Formats this.outputStream = stream; - int quality = this.quality.Clamp(1, 100); - // Convert from a quality rating to a scaling factor. int scale; if (this.quality < 50) { - scale = 5000 / quality; + scale = 5000 / this.quality; } else { - scale = 200 - (quality * 2); + scale = 200 - (this.quality * 2); } // Initialize the quantization tables. @@ -245,8 +237,8 @@ namespace ImageSharp.Formats } // Write the End Of Image marker. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.EOI; + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.EOI; stream.Write(this.buffer, 0, 2); stream.Flush(); } @@ -261,7 +253,7 @@ namespace ImageSharp.Formats private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F quant) { dqt[offset++] = (byte)i; - for (int j = 0; j < Block8x8F.ScalarCount; j++) + for (int j = 0; j < Block8x8F.Size; j++) { dqt[offset++] = (byte)quant[j]; } @@ -275,7 +267,7 @@ namespace ImageSharp.Formats /// The quantization table. private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) { - for (int j = 0; j < Block8x8F.ScalarCount; j++) + for (int j = 0; j < Block8x8F.Size; j++) { int x = UnscaledQuant[i, j]; x = ((x * scale) + 50) / 100; @@ -454,7 +446,7 @@ namespace ImageSharp.Formats Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; - UnzigData unzig = UnzigData.Create(); + ZigZag unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; @@ -507,12 +499,12 @@ namespace ImageSharp.Formats private void WriteApplicationHeader(short horizontalResolution, short verticalResolution) { // Write the start of image marker. Markers are always prefixed with with 0xff. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.SOI; + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.SOI; // Write the JFIF headers - this.buffer[2] = JpegConstants.Markers.XFF; - this.buffer[3] = JpegConstants.Markers.APP0; // Application Marker + this.buffer[2] = OrigJpegConstants.Markers.XFF; + this.buffer[3] = OrigJpegConstants.Markers.APP0; // Application Marker this.buffer[4] = 0x00; this.buffer[5] = 0x10; this.buffer[6] = 0x4a; // J @@ -561,9 +553,9 @@ namespace ImageSharp.Formats Block8x8F* quant, int* unzigPtr) { - DCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); + FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); - Block8x8F.UnzigDivRound(tempDest1, tempDest2, quant, unzigPtr); + Block8x8F.Quantize(tempDest1, tempDest2, quant, unzigPtr); float* unziggedDestPtr = (float*)tempDest2; int dc = (int)unziggedDestPtr[0]; @@ -575,7 +567,7 @@ namespace ImageSharp.Formats HuffIndex h = (HuffIndex)((2 * (int)index) + 1); int runLength = 0; - for (int zig = 1; zig < Block8x8F.ScalarCount; zig++) + for (int zig = 1; zig < Block8x8F.Size; zig++) { int ac = (int)unziggedDestPtr[zig]; @@ -626,7 +618,7 @@ namespace ImageSharp.Formats markerlen += 1 + 16 + s.Values.Length; } - this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen); + this.WriteMarkerHeader(OrigJpegConstants.Markers.DHT, markerlen); for (int i = 0; i < specs.Length; i++) { HuffmanSpec spec = specs[i]; @@ -659,12 +651,12 @@ namespace ImageSharp.Formats private void WriteDefineQuantizationTables() { // Marker + quantization table lengths - int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.ScalarCount)); - this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen); + int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size)); + this.WriteMarkerHeader(OrigJpegConstants.Markers.DQT, markerlen); // Loop through and collect the tables as one array. // This allows us to reduce the number of writes to the stream. - int dqtCount = (QuantizationTableCount * Block8x8F.ScalarCount) + QuantizationTableCount; + int dqtCount = (QuantizationTableCount * Block8x8F.Size) + QuantizationTableCount; byte[] dqt = ArrayPool.Shared.Rent(dqtCount); int offset = 0; @@ -698,8 +690,8 @@ namespace ImageSharp.Formats int length = data.Length + 2; - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.APP1; // Application Marker + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.APP1; // Application Marker this.buffer[2] = (byte)((length >> 8) & 0xFF); this.buffer[3] = (byte)(length & 0xFF); @@ -757,8 +749,8 @@ namespace ImageSharp.Formats dataLength -= length; - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.APP2; // Application Marker int markerLength = length + 16; this.buffer[2] = (byte)((markerLength >> 8) & 0xFF); this.buffer[3] = (byte)(markerLength & 0xFF); @@ -830,7 +822,7 @@ namespace ImageSharp.Formats // Length (high byte, low byte), 8 + components * 3. int markerlen = 8 + (3 * componentCount); - this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen); + this.WriteMarkerHeader(OrigJpegConstants.Markers.SOF0, markerlen); this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported this.buffer[1] = (byte)(height >> 8); this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported @@ -911,7 +903,7 @@ namespace ImageSharp.Formats Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; - UnzigData unzig = UnzigData.Create(); + ZigZag unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; @@ -973,7 +965,7 @@ namespace ImageSharp.Formats private void WriteMarkerHeader(byte marker, int length) { // Markers are always prefixed with with 0xff. - this.buffer[0] = JpegConstants.Markers.XFF; + this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[1] = marker; this.buffer[2] = (byte)(length >> 8); this.buffer[3] = (byte)(length & 0xff); diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs similarity index 74% rename from src/ImageSharp/Formats/Jpeg/JpegConstants.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs index 99c0399dcc..be383d2120 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort +{ /// /// Defines jpeg constants defined in the specification. /// - internal static class JpegConstants + internal static class OrigJpegConstants { /// /// The maximum allowable length in each dimension of a jpeg image. @@ -27,67 +25,6 @@ namespace ImageSharp.Formats /// public static readonly IEnumerable FileExtensions = new[] { "jpg", "jpeg", "jfif" }; - /// - /// Represents high detail chroma horizontal subsampling. - /// - public static readonly byte[] ChromaFourFourFourHorizontal = { 0x11, 0x11, 0x11 }; - - /// - /// Represents high detail chroma vertical subsampling. - /// - public static readonly byte[] ChromaFourFourFourVertical = { 0x11, 0x11, 0x11 }; - - /// - /// Represents medium detail chroma vertical subsampling. - /// - public static readonly byte[] ChromaFourTwoTwoVertical = { 0x11, 0x11, 0x11 }; - - /// - /// Represents low detail chroma vertical subsampling. - /// - public static readonly byte[] ChromaFourTwoZeroVertical = { 0x22, 0x11, 0x11 }; - - /// - /// Represents medium detail chroma horizontal subsampling. - /// - public static readonly byte[] ChromaFourTwoTwoHorizontal = { 0x22, 0x11, 0x11 }; - - /// - /// Represents low detail chroma horizontal subsampling. - /// - public static readonly byte[] ChromaFourTwoZeroHorizontal = { 0x22, 0x11, 0x11 }; - - /// - /// Describes component ids for start of frame components. - /// - internal static class Components - { - /// - /// The YCbCr luminance component id. - /// - public const byte Y = 1; - - /// - /// The YCbCr chroma component id. - /// - public const byte Cb = 2; - - /// - /// The YCbCr chroma component id. - /// - public const byte Cr = 3; - - /// - /// The YIQ x coordinate component id. - /// - public const byte I = 4; - - /// - /// The YIQ y coordinate component id. - /// - public const byte Q = 5; - } - /// /// Describes common Jpeg markers /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs new file mode 100644 index 0000000000..ecebe9480d --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort +{ + /// + /// Image decoder for generating an image out of a jpg stream. + /// + internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector + { + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; set; } + + /// + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + using (var decoder = new OrigJpegDecoderCore(configuration, this)) + { + return decoder.Decode(stream); + } + } + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + using (var decoder = new OrigJpegDecoderCore(configuration, this)) + { + return decoder.Identify(stream); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs new file mode 100644 index 0000000000..cdc2a91972 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -0,0 +1,799 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.IO; + +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort +{ + /// + /// + /// Performs the jpeg decoding operation. + /// + internal sealed unsafe class OrigJpegDecoderCore : IRawJpegData + { + /// + /// The maximum number of color components + /// + public const int MaxComponents = 4; + + /// + /// The maximum number of quantization tables + /// + public const int MaxTq = 3; + + /// + /// The only supported precision + /// + public const int SupportedPrecision = 8; + + // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P +#pragma warning disable SA1401 // FieldsMustBePrivate + + /// + /// Encapsulates stream reading and processing data and operations for . + /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s + /// + public InputProcessor InputProcessor; +#pragma warning restore SA401 + + /// + /// The global configuration + /// + private readonly Configuration configuration; + + /// + /// Whether the image has a JFIF header + /// It's faster to check this than to use the equality operator on the struct + /// + private bool isJFif; + + /// + /// Contains information about the JFIF marker + /// + private JFifMarker jFif; + + /// + /// Whether the image has a EXIF header + /// + private bool isExif; + + /// + /// Whether the image has an Adobe marker. + /// It's faster to check this than to use the equality operator on the struct + /// + private bool isAdobe; + + /// + /// Contains information about the Adobe marker + /// + private AdobeMarker adobe; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) + { + this.IgnoreMetadata = options.IgnoreMetadata; + this.configuration = configuration ?? Configuration.Default; + this.HuffmanTrees = OrigHuffmanTree.CreateHuffmanTrees(); + this.QuantizationTables = new Block8x8F[MaxTq + 1]; + this.Temp = new byte[2 * Block8x8F.Size]; + } + + /// + public JpegColorSpace ColorSpace { get; private set; } + + /// + /// Gets the component array + /// + public OrigComponent[] Components { get; private set; } + + /// + /// Gets the huffman trees + /// + public OrigHuffmanTree[] HuffmanTrees { get; } + + /// + public Block8x8F[] QuantizationTables { get; } + + /// + /// Gets the temporary buffer used to store bytes read from the stream. + /// TODO: Should be stack allocated, fixed sized buffer! + /// + public byte[] Temp { get; } + + /// + public Size ImageSizeInPixels { get; private set; } + + /// + /// Gets the number of MCU blocks in the image as . + /// + public Size ImageSizeInMCU { get; private set; } + + /// + public int ComponentCount { get; private set; } + + IEnumerable IRawJpegData.Components => this.Components; + + /// + /// Gets the color depth, in number of bits per pixel. + /// + public int BitsPerPixel => this.ComponentCount * SupportedPrecision; + + /// + /// Gets the image height + /// + public int ImageHeight => this.ImageSizeInPixels.Height; + + /// + /// Gets the image width + /// + public int ImageWidth => this.ImageSizeInPixels.Width; + + /// + /// Gets the input stream. + /// + public Stream InputStream { get; private set; } + + /// + /// Gets a value indicating whether the image is interlaced (progressive) + /// + public bool IsProgressive { get; private set; } + + /// + /// Gets the restart interval + /// + public int RestartInterval { get; private set; } + + /// + /// Gets the number of MCU-s (Minimum Coded Units) in the image along the X axis + /// + public int MCUCountX => this.ImageSizeInMCU.Width; + + /// + /// Gets the number of MCU-s (Minimum Coded Units) in the image along the Y axis + /// + public int MCUCountY => this.ImageSizeInMCU.Height; + + /// + /// Gets the the total number of MCU-s (Minimum Coded Units) in the image. + /// + public int TotalMCUCount => this.MCUCountX * this.MCUCountY; + + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; } + + /// + /// Gets the decoded by this decoder instance. + /// + public ImageMetaData MetaData { get; private set; } + + /// + /// Decodes the image from the specified and sets + /// the data to image. + /// + /// The pixel format. + /// The stream, where the image should be. + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : struct, IPixel + { + this.ParseStream(stream); + + return this.PostProcessIntoImage(); + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public IImageInfo Identify(Stream stream) + { + this.ParseStream(stream, true); + + return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); + } + + /// + public void Dispose() + { + for (int i = 0; i < this.HuffmanTrees.Length; i++) + { + this.HuffmanTrees[i].Dispose(); + } + + if (this.Components != null) + { + foreach (OrigComponent component in this.Components) + { + component.Dispose(); + } + } + + this.InputProcessor.Dispose(); + } + + /// + /// Read metadata from stream and read the blocks in the scans into . + /// + /// The stream + /// Whether to decode metadata only. + public void ParseStream(Stream stream, bool metadataOnly = false) + { + this.MetaData = new ImageMetaData(); + this.InputStream = stream; + this.InputProcessor = new InputProcessor(stream, this.Temp); + + // Check for the Start Of Image marker. + this.InputProcessor.ReadFull(this.Temp, 0, 2); + if (this.Temp[0] != OrigJpegConstants.Markers.XFF || this.Temp[1] != OrigJpegConstants.Markers.SOI) + { + throw new ImageFormatException("Missing SOI marker."); + } + + // Process the remaining segments until the End Of Image marker. + bool processBytes = true; + + // we can't currently short circute progressive images so don't try. + while (processBytes) + { + this.InputProcessor.ReadFull(this.Temp, 0, 2); + while (this.Temp[0] != 0xff) + { + // Strictly speaking, this is a format error. However, libjpeg is + // liberal in what it accepts. As of version 9, next_marker in + // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and + // continues to decode the stream. Even before next_marker sees + // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many + // bytes as it can, possibly past the end of a scan's data. It + // effectively puts back any markers that it overscanned (e.g. an + // "\xff\xd9" EOI marker), but it does not put back non-marker data, + // and thus it can silently ignore a small number of extraneous + // non-marker bytes before next_marker has a chance to see them (and + // print a warning). + // We are therefore also liberal in what we accept. Extraneous data + // is silently ignore + // This is similar to, but not exactly the same as, the restart + // mechanism within a scan (the RST[0-7] markers). + // Note that extraneous 0xff bytes in e.g. SOS data are escaped as + // "\xff\x00", and so are detected a little further down below. + this.Temp[0] = this.Temp[1]; + this.Temp[1] = this.InputProcessor.ReadByte(); + } + + byte marker = this.Temp[1]; + if (marker == 0) + { + // Treat "\xff\x00" as extraneous data. + continue; + } + + while (marker == 0xff) + { + // Section B.1.1.2 says, "Any marker may optionally be preceded by any + // number of fill bytes, which are bytes assigned code X'FF'". + marker = this.InputProcessor.ReadByte(); + } + + // End Of Image. + if (marker == OrigJpegConstants.Markers.EOI) + { + break; + } + + if (marker >= OrigJpegConstants.Markers.RST0 && marker <= OrigJpegConstants.Markers.RST7) + { + // Figures B.2 and B.16 of the specification suggest that restart markers should + // only occur between Entropy Coded Segments and not after the final ECS. + // However, some encoders may generate incorrect JPEGs with a final restart + // marker. That restart marker will be seen here instead of inside the ProcessSOS + // method, and is ignored as a harmless error. Restart markers have no extra data, + // so we check for this before we read the 16-bit length of the segment. + continue; + } + + // Read the 16-bit length of the segment. The value includes the 2 bytes for the + // length itself, so we subtract 2 to get the number of remaining bytes. + this.InputProcessor.ReadFull(this.Temp, 0, 2); + int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; + if (remaining < 0) + { + throw new ImageFormatException("Short segment length."); + } + + switch (marker) + { + case OrigJpegConstants.Markers.SOF0: + case OrigJpegConstants.Markers.SOF1: + case OrigJpegConstants.Markers.SOF2: + this.IsProgressive = marker == OrigJpegConstants.Markers.SOF2; + this.ProcessStartOfFrameMarker(remaining); + if (metadataOnly && this.isJFif) + { + return; + } + + break; + case OrigJpegConstants.Markers.DHT: + if (metadataOnly) + { + this.InputProcessor.Skip(remaining); + } + else + { + this.ProcessDefineHuffmanTablesMarker(remaining); + } + + break; + case OrigJpegConstants.Markers.DQT: + if (metadataOnly) + { + this.InputProcessor.Skip(remaining); + } + else + { + this.ProcessDefineQuantizationTablesMarker(remaining); + } + + break; + case OrigJpegConstants.Markers.SOS: + if (metadataOnly) + { + return; + } + + // when this is a progressive image this gets called a number of times + // need to know how many times this should be called in total. + this.ProcessStartOfScanMarker(remaining); + if (this.InputProcessor.ReachedEOF || !this.IsProgressive) + { + // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data. + processBytes = false; + } + + break; + case OrigJpegConstants.Markers.DRI: + if (metadataOnly) + { + this.InputProcessor.Skip(remaining); + } + else + { + this.ProcessDefineRestartIntervalMarker(remaining); + } + + break; + case OrigJpegConstants.Markers.APP0: + this.ProcessApplicationHeaderMarker(remaining); + break; + case OrigJpegConstants.Markers.APP1: + this.ProcessApp1Marker(remaining); + break; + case OrigJpegConstants.Markers.APP2: + this.ProcessApp2Marker(remaining); + break; + case OrigJpegConstants.Markers.APP14: + this.ProcessApp14Marker(remaining); + break; + default: + if ((marker >= OrigJpegConstants.Markers.APP0 && marker <= OrigJpegConstants.Markers.APP15) + || marker == OrigJpegConstants.Markers.COM) + { + this.InputProcessor.Skip(remaining); + } + else if (marker < OrigJpegConstants.Markers.SOF0) + { + // See Table B.1 "Marker code assignments". + throw new ImageFormatException("Unknown marker"); + } + else + { + throw new ImageFormatException("Unknown marker"); + } + + break; + } + } + + this.InitDerivedMetaDataProperties(); + } + + /// + /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. + /// + private void InitDerivedMetaDataProperties() + { + if (this.isExif) + { + ExifValue horizontal = this.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + ExifValue vertical = this.MetaData.ExifProfile.GetValue(ExifTag.YResolution); + double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; + double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; + + if (horizontalValue > 0 && verticalValue > 0) + { + this.MetaData.HorizontalResolution = horizontalValue; + this.MetaData.VerticalResolution = verticalValue; + } + } + else if (this.isJFif) + { + this.MetaData.HorizontalResolution = this.jFif.XDensity; + this.MetaData.VerticalResolution = this.jFif.YDensity; + } + } + + /// + /// Processes the application header containing the JFIF identifier plus extra data. + /// + /// The remaining bytes in the segment block. + private void ProcessApplicationHeaderMarker(int remaining) + { + if (remaining < 5) + { + this.InputProcessor.Skip(remaining); + return; + } + + const int MarkerLength = JFifMarker.Length; + this.InputProcessor.ReadFull(this.Temp, 0, MarkerLength); + remaining -= MarkerLength; + + this.isJFif = JFifMarker.TryParse(this.Temp, out this.jFif); + + if (remaining > 0) + { + this.InputProcessor.Skip(remaining); + } + } + + /// + /// Processes the App1 marker retrieving any stored metadata + /// + /// The remaining bytes in the segment block. + private void ProcessApp1Marker(int remaining) + { + if (remaining < 6 || this.IgnoreMetadata) + { + this.InputProcessor.Skip(remaining); + return; + } + + byte[] profile = new byte[remaining]; + this.InputProcessor.ReadFull(profile, 0, remaining); + + if (ProfileResolver.IsProfile(profile, ProfileResolver.ExifMarker)) + { + this.isExif = true; + this.MetaData.ExifProfile = new ExifProfile(profile); + } + } + + /// + /// Processes the App2 marker retrieving any stored ICC profile information + /// + /// The remaining bytes in the segment block. + private void ProcessApp2Marker(int remaining) + { + // Length is 14 though we only need to check 12. + const int Icclength = 14; + if (remaining < Icclength || this.IgnoreMetadata) + { + this.InputProcessor.Skip(remaining); + return; + } + + byte[] identifier = new byte[Icclength]; + this.InputProcessor.ReadFull(identifier, 0, Icclength); + remaining -= Icclength; // We have read it by this point + + if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker)) + { + byte[] profile = new byte[remaining]; + this.InputProcessor.ReadFull(profile, 0, remaining); + + if (this.MetaData.IccProfile == null) + { + this.MetaData.IccProfile = new IccProfile(profile); + } + else + { + this.MetaData.IccProfile.Extend(profile); + } + } + else + { + // Not an ICC profile we can handle. Skip the remaining bytes so we can carry on and ignore this. + this.InputProcessor.Skip(remaining); + } + } + + /// + /// Processes the application header containing the Adobe identifier + /// which stores image encoding information for DCT filters. + /// + /// The remaining bytes in the segment block. + private void ProcessApp14Marker(int remaining) + { + const int MarkerLength = AdobeMarker.Length; + if (remaining < MarkerLength) + { + // Skip the application header length + this.InputProcessor.Skip(remaining); + return; + } + + this.InputProcessor.ReadFull(this.Temp, 0, MarkerLength); + remaining -= MarkerLength; + + this.isAdobe = AdobeMarker.TryParse(this.Temp, out this.adobe); + + if (remaining > 0) + { + this.InputProcessor.Skip(remaining); + } + } + + /// + /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. + /// + /// The remaining bytes in the segment block. + /// + /// Thrown if the tables do not match the header + /// + private void ProcessDefineQuantizationTablesMarker(int remaining) + { + while (remaining > 0) + { + bool done = false; + + remaining--; + byte x = this.InputProcessor.ReadByte(); + int tq = x & 0x0F; + if (tq > MaxTq) + { + throw new ImageFormatException("Bad Tq value"); + } + + switch (x >> 4) + { + case 0: + if (remaining < Block8x8F.Size) + { + done = true; + break; + } + + remaining -= Block8x8F.Size; + this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.Size); + + for (int i = 0; i < Block8x8F.Size; i++) + { + this.QuantizationTables[tq][i] = this.Temp[i]; + } + + break; + case 1: + if (remaining < 2 * Block8x8F.Size) + { + done = true; + break; + } + + remaining -= 2 * Block8x8F.Size; + this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.Size); + + for (int i = 0; i < Block8x8F.Size; i++) + { + this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; + } + + break; + default: + throw new ImageFormatException("Bad Pq value"); + } + + if (done) + { + break; + } + } + + if (remaining != 0) + { + throw new ImageFormatException("DQT has wrong length"); + } + } + + /// + /// Processes the Start of Frame marker. Specified in section B.2.2. + /// + /// The remaining bytes in the segment block. + private void ProcessStartOfFrameMarker(int remaining) + { + if (this.ComponentCount != 0) + { + throw new ImageFormatException("Multiple SOF markers"); + } + + switch (remaining) + { + case 6 + (3 * 1): // Grayscale image. + this.ComponentCount = 1; + break; + case 6 + (3 * 3): // YCbCr or RGB image. + this.ComponentCount = 3; + break; + case 6 + (3 * 4): // YCbCrK or CMYK image. + this.ComponentCount = 4; + break; + default: + throw new ImageFormatException("Incorrect number of components"); + } + + this.InputProcessor.ReadFull(this.Temp, 0, remaining); + + // We only support 8-bit precision. + if (this.Temp[0] != SupportedPrecision) + { + throw new ImageFormatException("Only 8-Bit precision supported."); + } + + int height = (this.Temp[1] << 8) + this.Temp[2]; + int width = (this.Temp[3] << 8) + this.Temp[4]; + + this.ImageSizeInPixels = new Size(width, height); + + if (this.Temp[5] != this.ComponentCount) + { + throw new ImageFormatException("SOF has wrong length"); + } + + this.Components = new OrigComponent[this.ComponentCount]; + + for (int i = 0; i < this.ComponentCount; i++) + { + byte componentIdentifier = this.Temp[6 + (3 * i)]; + var component = new OrigComponent(componentIdentifier, i); + component.InitializeCoreData(this); + this.Components[i] = component; + } + + int h0 = this.Components[0].HorizontalSamplingFactor; + int v0 = this.Components[0].VerticalSamplingFactor; + + this.ImageSizeInMCU = this.ImageSizeInPixels.DivideRoundUp(8 * h0, 8 * v0); + + foreach (OrigComponent component in this.Components) + { + component.InitializeDerivedData(this); + } + + this.ColorSpace = this.DeduceJpegColorSpace(); + } + + /// + /// Processes a Define Huffman Table marker, and initializes a huffman + /// struct from its contents. Specified in section B.2.4.2. + /// + /// The remaining bytes in the segment block. + private void ProcessDefineHuffmanTablesMarker(int remaining) + { + while (remaining > 0) + { + if (remaining < 17) + { + throw new ImageFormatException("DHT has wrong length"); + } + + this.InputProcessor.ReadFull(this.Temp, 0, 17); + + int tc = this.Temp[0] >> 4; + if (tc > OrigHuffmanTree.MaxTc) + { + throw new ImageFormatException("Bad Tc value"); + } + + int th = this.Temp[0] & 0x0f; + if (th > OrigHuffmanTree.MaxTh) + { + throw new ImageFormatException("Bad Th value"); + } + + int huffTreeIndex = (tc * OrigHuffmanTree.ThRowSize) + th; + this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( + ref this.InputProcessor, + this.Temp, + ref remaining); + } + } + + /// + /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in + /// macroblocks + /// + /// The remaining bytes in the segment block. + private void ProcessDefineRestartIntervalMarker(int remaining) + { + if (remaining != 2) + { + throw new ImageFormatException("DRI has wrong length"); + } + + this.InputProcessor.ReadFull(this.Temp, 0, remaining); + this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; + } + + /// + /// Processes the SOS (Start of scan marker). + /// + /// The remaining bytes in the segment block. + /// + /// Missing SOF Marker + /// SOS has wrong length + /// + private void ProcessStartOfScanMarker(int remaining) + { + var scan = default(OrigJpegScanDecoder); + OrigJpegScanDecoder.InitStreamReading(&scan, this, remaining); + this.InputProcessor.Bits = default(Bits); + scan.DecodeBlocks(this); + } + + private JpegColorSpace DeduceJpegColorSpace() + { + switch (this.ComponentCount) + { + case 1: + return JpegColorSpace.GrayScale; + case 3: + if (!this.isAdobe || this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformYCbCr) + { + return JpegColorSpace.YCbCr; + } + + if (this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformUnknown) + { + return JpegColorSpace.RGB; + } + + break; + case 4: + if (this.adobe.ColorTransform == OrigJpegConstants.Adobe.ColorTransformYcck) + { + return JpegColorSpace.Ycck; + } + + return JpegColorSpace.Cmyk; + } + + throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}." + + "JpegDecoder only supports YCbCr, RGB, YccK, CMYK and Grayscale color spaces."); + } + + private Image PostProcessIntoImage() + where TPixel : struct, IPixel + { + using (var postProcessor = new JpegImagePostProcessor(this)) + { + var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); + postProcessor.PostProcess(image.Frames.RootFrame); + return image; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs index afb8e07007..01ed5063ba 100644 --- a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils +{ /// /// Jpeg specific utilities and extension methods /// - internal static unsafe class JpegUtils + internal static class OrigJpegUtils { /// /// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image. diff --git a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs index 6830e2e4a5..880a7f7a3f 100644 --- a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Image decoder for generating an image out of a jpg stream. /// diff --git a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs index 947c98ee2a..a84652cefe 100644 --- a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Encoder for writing the data image to a stream in jpeg format. /// diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index 8fbf9e5a74..9cd7b3a8bd 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,14 +22,9 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsJpeg(this Image source, Stream stream) + public static void SaveAsJpeg(this Image source, Stream stream) where TPixel : struct, IPixel - { - return SaveAsJpeg(source, stream, null); - } + => SaveAsJpeg(source, stream, null); /// /// Saves the image to the given stream with the jpeg format. @@ -41,16 +34,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) + public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) where TPixel : struct, IPixel - { - encoder = encoder ?? new JpegEncoder(); - encoder.Encode(source, stream); - - return source; - } + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Jpeg)); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs index bb8c4e83f2..1ab5093398 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Jpeg { /// /// Registers the image encoders, decoders and mime type detectors for the jpeg format. diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index b3caddeca7..91835b5d71 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; +using System.IO; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Image decoder for generating an image out of a jpg stream. /// - public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions + public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -25,12 +22,23 @@ namespace ImageSharp.Formats public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { - Guard.NotNull(stream, "stream"); + Guard.NotNull(stream, nameof(stream)); - using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this)) + using (var decoder = new OrigJpegDecoderCore(configuration, this)) { return decoder.Decode(stream); } } + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, "stream"); + + using (var decoder = new OrigJpegDecoderCore(configuration, this)) + { + return decoder.Identify(stream); + } + } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs deleted file mode 100644 index 0ce927e516..0000000000 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ /dev/null @@ -1,1374 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - - using ImageSharp.Formats.Jpg; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Performs the jpeg decoding operation. - /// - internal sealed unsafe class JpegDecoderCore : IDisposable - { - /// - /// The maximum number of color components - /// - public const int MaxComponents = 4; - - /// - /// The maximum number of quantization tables - /// - public const int MaxTq = 3; - - // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P -#pragma warning disable SA1401 // FieldsMustBePrivate - - /// - /// Encapsulates stream reading and processing data and operations for . - /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s - /// - public InputProcessor InputProcessor; -#pragma warning restore SA401 - - /// - /// Lookup tables for converting YCbCr to Rgb - /// - private static YCbCrToRgbTables yCbCrToRgbTables = YCbCrToRgbTables.Create(); - - /// - /// The global configuration - /// - private readonly Configuration configuration; - - /// - /// The App14 marker color-space - /// - private byte adobeTransform; - - /// - /// Whether the image is in CMYK format with an App14 marker - /// - private bool adobeTransformValid; - - /// - /// The black image to decode to. - /// - private JpegPixelArea blackImage; - - /// - /// A grayscale image to decode to. - /// - private JpegPixelArea grayImage; - - /// - /// The horizontal resolution. Calculated if the image has a JFIF header. - /// - private short horizontalResolution; - - /// - /// Whether the image has a JFIF header - /// - private bool isJfif; - - /// - /// Whether the image has a EXIF header - /// - private bool isExif; - - /// - /// The vertical resolution. Calculated if the image has a JFIF header. - /// - private short verticalResolution; - - /// - /// The full color image to decode to. - /// - private YCbCrImage ycbcrImage; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) - { - this.IgnoreMetadata = options.IgnoreMetadata; - this.configuration = configuration ?? Configuration.Default; - this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees(); - this.QuantizationTables = new Block8x8F[MaxTq + 1]; - this.Temp = new byte[2 * Block8x8F.ScalarCount]; - this.ComponentArray = new Component[MaxComponents]; - this.DecodedBlocks = new Buffer[MaxComponents]; - } - - /// - /// Gets the component array - /// - public Component[] ComponentArray { get; } - - /// - /// Gets the huffman trees - /// - public HuffmanTree[] HuffmanTrees { get; } - - /// - /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. - /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. - /// This is done by . - /// When ==true, we are touching these blocks multiple times - each time we process a Scan. - /// - public Buffer[] DecodedBlocks { get; } - - /// - /// Gets the quantization tables, in zigzag order. - /// - public Block8x8F[] QuantizationTables { get; } - - /// - /// Gets the temporary buffer used to store bytes read from the stream. - /// TODO: Should be stack allocated, fixed sized buffer! - /// - public byte[] Temp { get; } - - /// - /// Gets the number of color components within the image. - /// - public int ComponentCount { get; private set; } - - /// - /// Gets the image height - /// - public int ImageHeight { get; private set; } - - /// - /// Gets the image width - /// - public int ImageWidth { get; private set; } - - /// - /// Gets the input stream. - /// - public Stream InputStream { get; private set; } - - /// - /// Gets a value indicating whether the image is interlaced (progressive) - /// - public bool IsProgressive { get; private set; } - - /// - /// Gets the restart interval - /// - public int RestartInterval { get; private set; } - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the X axis - /// - public int MCUCountX { get; private set; } - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the Y axis - /// - public int MCUCountY { get; private set; } - - /// - /// Gets the the total number of MCU-s (Minimum Coded Units) in the image. - /// - public int TotalMCUCount => this.MCUCountX * this.MCUCountY; - - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; private set; } - - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. - public Image Decode(Stream stream) - where TPixel : struct, IPixel - { - ImageMetaData metadata = new ImageMetaData(); - this.ProcessStream(metadata, stream, false); - this.ProcessBlocksIntoJpegImageChannels(); - Image image = this.ConvertJpegPixelsToImagePixels(metadata); - - return image; - } - - /// - /// Dispose - /// - public void Dispose() - { - for (int i = 0; i < this.HuffmanTrees.Length; i++) - { - this.HuffmanTrees[i].Dispose(); - } - - foreach (Buffer blockArray in this.DecodedBlocks) - { - blockArray?.Dispose(); - } - - this.ycbcrImage?.Dispose(); - this.InputProcessor.Dispose(); - this.grayImage.Pixels?.Dispose(); - this.blackImage.Pixels?.Dispose(); - } - - /// - /// Gets the representing the channel at a given component index - /// - /// The component index - /// The of the channel - public JpegPixelArea GetDestinationChannel(int compIndex) - { - if (this.ComponentCount == 1) - { - return this.grayImage; - } - else - { - switch (compIndex) - { - case 0: - return new JpegPixelArea(this.ycbcrImage.YChannel); - case 1: - return new JpegPixelArea(this.ycbcrImage.CbChannel); - case 2: - return new JpegPixelArea(this.ycbcrImage.CrChannel); - case 3: - return this.blackImage; - default: - throw new ImageFormatException("Too many components"); - } - } - } - - /// - /// Read metadata from stream and read the blocks in the scans into . - /// - /// The metadata - /// The stream - /// Whether to decode metadata only. - private void ProcessStream(ImageMetaData metadata, Stream stream, bool metadataOnly) - { - this.InputStream = stream; - this.InputProcessor = new InputProcessor(stream, this.Temp); - - // Check for the Start Of Image marker. - this.InputProcessor.ReadFull(this.Temp, 0, 2); - if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI) - { - throw new ImageFormatException("Missing SOI marker."); - } - - // Process the remaining segments until the End Of Image marker. - bool processBytes = true; - - // we can't currently short circute progressive images so don't try. - while (processBytes) - { - this.InputProcessor.ReadFull(this.Temp, 0, 2); - while (this.Temp[0] != 0xff) - { - // Strictly speaking, this is a format error. However, libjpeg is - // liberal in what it accepts. As of version 9, next_marker in - // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and - // continues to decode the stream. Even before next_marker sees - // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many - // bytes as it can, possibly past the end of a scan's data. It - // effectively puts back any markers that it overscanned (e.g. an - // "\xff\xd9" EOI marker), but it does not put back non-marker data, - // and thus it can silently ignore a small number of extraneous - // non-marker bytes before next_marker has a chance to see them (and - // print a warning). - // We are therefore also liberal in what we accept. Extraneous data - // is silently ignore - // This is similar to, but not exactly the same as, the restart - // mechanism within a scan (the RST[0-7] markers). - // Note that extraneous 0xff bytes in e.g. SOS data are escaped as - // "\xff\x00", and so are detected a little further down below. - this.Temp[0] = this.Temp[1]; - this.Temp[1] = this.InputProcessor.ReadByte(); - } - - byte marker = this.Temp[1]; - if (marker == 0) - { - // Treat "\xff\x00" as extraneous data. - continue; - } - - while (marker == 0xff) - { - // Section B.1.1.2 says, "Any marker may optionally be preceded by any - // number of fill bytes, which are bytes assigned code X'FF'". - marker = this.InputProcessor.ReadByte(); - } - - // End Of Image. - if (marker == JpegConstants.Markers.EOI) - { - break; - } - - if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7) - { - // Figures B.2 and B.16 of the specification suggest that restart markers should - // only occur between Entropy Coded Segments and not after the final ECS. - // However, some encoders may generate incorrect JPEGs with a final restart - // marker. That restart marker will be seen here instead of inside the ProcessSOS - // method, and is ignored as a harmless error. Restart markers have no extra data, - // so we check for this before we read the 16-bit length of the segment. - continue; - } - - // Read the 16-bit length of the segment. The value includes the 2 bytes for the - // length itself, so we subtract 2 to get the number of remaining bytes. - this.InputProcessor.ReadFull(this.Temp, 0, 2); - int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; - if (remaining < 0) - { - throw new ImageFormatException("Short segment length."); - } - - switch (marker) - { - case JpegConstants.Markers.SOF0: - case JpegConstants.Markers.SOF1: - case JpegConstants.Markers.SOF2: - this.IsProgressive = marker == JpegConstants.Markers.SOF2; - this.ProcessStartOfFrameMarker(remaining); - if (metadataOnly && this.isJfif) - { - return; - } - - break; - case JpegConstants.Markers.DHT: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineHuffmanTablesMarker(remaining); - } - - break; - case JpegConstants.Markers.DQT: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDqt(remaining); - } - - break; - case JpegConstants.Markers.SOS: - if (metadataOnly) - { - return; - } - - // when this is a progressive image this gets called a number of times - // need to know how many times this should be called in total. - this.ProcessStartOfScan(remaining); - if (this.InputProcessor.UnexpectedEndOfStreamReached || !this.IsProgressive) - { - // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data. - processBytes = false; - } - - break; - case JpegConstants.Markers.DRI: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineRestartIntervalMarker(remaining); - } - - break; - case JpegConstants.Markers.APP0: - this.ProcessApplicationHeader(remaining); - break; - case JpegConstants.Markers.APP1: - this.ProcessApp1Marker(remaining, metadata); - break; - case JpegConstants.Markers.APP2: - this.ProcessApp2Marker(remaining, metadata); - break; - case JpegConstants.Markers.APP14: - this.ProcessApp14Marker(remaining); - break; - default: - if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15) - || marker == JpegConstants.Markers.COM) - { - this.InputProcessor.Skip(remaining); - } - else if (marker < JpegConstants.Markers.SOF0) - { - // See Table B.1 "Marker code assignments". - throw new ImageFormatException("Unknown marker"); - } - else - { - throw new ImageFormatException("Unknown marker"); - } - - break; - } - } - } - - /// - /// Processes the SOS (Start of scan marker). - /// - /// The remaining bytes in the segment block. - /// - /// Missing SOF Marker - /// SOS has wrong length - /// - private void ProcessStartOfScan(int remaining) - { - JpegScanDecoder scan = default(JpegScanDecoder); - JpegScanDecoder.InitStreamReading(&scan, this, remaining); - this.InputProcessor.Bits = default(Bits); - this.MakeImage(); - scan.DecodeBlocks(this); - } - - /// - /// Process the blocks in into Jpeg image channels ( and ) - /// are in a "raw" frequency-domain form. We need to apply IDCT, dequantization and unzigging to transform them into color-space blocks. - /// We can copy these blocks into -s afterwards. - /// - /// The pixel type - private void ProcessBlocksIntoJpegImageChannels() - where TPixel : struct, IPixel - { - Parallel.For( - 0, - this.ComponentCount, - componentIndex => - { - JpegBlockProcessor processor = default(JpegBlockProcessor); - JpegBlockProcessor.Init(&processor, componentIndex); - processor.ProcessAllBlocks(this); - }); - } - - /// - /// Convert the pixel data in and/or into pixels of - /// - /// The pixel type - /// The metadata for the image. - /// The decoded image. - private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) - where TPixel : struct, IPixel - { - Image image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); - - if (this.grayImage.IsInitialized) - { - this.ConvertFromGrayScale(image); - return image; - } - else if (this.ycbcrImage != null) - { - if (this.ComponentCount == 4) - { - if (!this.adobeTransformValid) - { - throw new ImageFormatException( - "Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata"); - } - - // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe - // See https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html - // TODO: YCbCrA? - if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck) - { - this.ConvertFromYcck(image); - } - else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) - { - // Assume CMYK - this.ConvertFromCmyk(image); - } - - return image; - } - - if (this.ComponentCount == 3) - { - if (this.IsRGB()) - { - this.ConvertFromRGB(image); - return image; - } - - this.ConvertFromYCbCr(image); - return image; - } - - throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces."); - } - else - { - throw new ImageFormatException("Missing SOS marker."); - } - } - - /// - /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header. - /// - /// The pixel format. - /// The image to assign the resolution to. - private void AssignResolution(Image image) - where TPixel : struct, IPixel - { - if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) - { - image.MetaData.HorizontalResolution = this.horizontalResolution; - image.MetaData.VerticalResolution = this.verticalResolution; - } - else if (this.isExif) - { - ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); - ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); - double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; - double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; - - if (horizontalValue > 0 && verticalValue > 0) - { - image.MetaData.HorizontalResolution = horizontalValue; - image.MetaData.VerticalResolution = verticalValue; - } - } - } - - /// - /// Converts the image from the original CMYK image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromCmyk(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < image.Width; x++) - { - byte cyan = this.ycbcrImage.YChannel[yo + x]; - byte magenta = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte yellow = this.ycbcrImage.CrChannel[co + (x / scale)]; - - TPixel packed = default(TPixel); - this.PackCmyk(ref packed, cyan, magenta, yellow, x, y); - pixels[x, y] = packed; - } - }); - } - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original grayscale image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromGrayScale(Image image) - where TPixel : struct, IPixel - { - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); - - int yoff = this.grayImage.GetRowOffset(y); - - for (int x = 0; x < image.Width; x++) - { - byte rgb = this.grayImage.Pixels[yoff + x]; - ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); - pixel.PackFromRgba32(new Rgba32(rgb, rgb, rgb, 255)); - } - }); - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original RBG image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromRGB(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); - - Rgba32 rgba = new Rgba32(0, 0, 0, 255); - - for (int x = 0; x < image.Width; x++) - { - rgba.R = this.ycbcrImage.YChannel[yo + x]; - rgba.G = this.ycbcrImage.CbChannel[co + (x / scale)]; - rgba.B = this.ycbcrImage.CrChannel[co + (x / scale)]; - - ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); - pixel.PackFromRgba32(rgba); - } - }); - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original YCbCr image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromYCbCr(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - // TODO. This Parallel loop doesn't give us the boost it should. - ref byte ycRef = ref this.ycbcrImage.YChannel[0]; - ref byte cbRef = ref this.ycbcrImage.CbChannel[0]; - ref byte crRef = ref this.ycbcrImage.CrChannel[0]; - fixed (YCbCrToRgbTables* tables = &yCbCrToRgbTables) - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < image.Width; x++) - { - int cOff = co + (x / scale); - byte yy = Unsafe.Add(ref ycRef, yo + x); - byte cb = Unsafe.Add(ref cbRef, cOff); - byte cr = Unsafe.Add(ref crRef, cOff); - - TPixel packed = default(TPixel); - YCbCrToRgbTables.Pack(ref packed, tables, yy, cb, cr); - pixels[x, y] = packed; - } - } - }); - } - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original YCCK image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromYcck(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - - Parallel.For( - 0, - image.Height, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); - - for (int x = 0; x < image.Width; x++) - { - byte yy = this.ycbcrImage.YChannel[yo + x]; - byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; - - ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); - this.PackYcck(ref pixel, yy, cb, cr, x, y); - } - }); - - this.AssignResolution(image); - } - - /// - /// Returns a value indicating whether the image in an RGB image. - /// - /// - /// The . - /// - private bool IsRGB() - { - if (this.isJfif) - { - return false; - } - - if (this.adobeTransformValid && this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) - { - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe - // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr. - return true; - } - - return this.ComponentArray[0].Identifier == 'R' && this.ComponentArray[1].Identifier == 'G' - && this.ComponentArray[2].Identifier == 'B'; - } - - /// - /// Makes the image from the buffer. - /// - private void MakeImage() - { - if (this.grayImage.IsInitialized || this.ycbcrImage != null) - { - return; - } - - if (this.ComponentCount == 1) - { - Buffer2D buffer = Buffer2D.CreateClean(8 * this.MCUCountX, 8 * this.MCUCountY); - this.grayImage = new JpegPixelArea(buffer); - } - else - { - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; - int horizontalRatio = h0 / this.ComponentArray[1].HorizontalFactor; - int verticalRatio = v0 / this.ComponentArray[1].VerticalFactor; - - YCbCrImage.YCbCrSubsampleRatio ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; - switch ((horizontalRatio << 4) | verticalRatio) - { - case 0x11: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; - break; - case 0x12: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440; - break; - case 0x21: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422; - break; - case 0x22: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420; - break; - case 0x41: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411; - break; - case 0x42: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410; - break; - } - - this.ycbcrImage = new YCbCrImage(8 * h0 * this.MCUCountX, 8 * v0 * this.MCUCountY, ratio); - - if (this.ComponentCount == 4) - { - int h3 = this.ComponentArray[3].HorizontalFactor; - int v3 = this.ComponentArray[3].VerticalFactor; - - Buffer2D buffer = Buffer2D.CreateClean(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY); - this.blackImage = new JpegPixelArea(buffer); - } - } - } - - /// - /// Optimized method to pack bytes to the image from the CMYK color space. - /// This is faster than implicit casting as it avoids double packing. - /// - /// The pixel format. - /// The packed pixel. - /// The cyan component. - /// The magenta component. - /// The yellow component. - /// The x-position within the image. - /// The y-position within the image. - private void PackCmyk(ref TPixel packed, byte c, byte m, byte y, int xx, int yy) - where TPixel : struct, IPixel - { - // Get keyline - float keyline = (255 - this.blackImage[xx, yy]) / 255F; - - // Convert back to RGB. CMY are not inverted - byte r = (byte)(((c / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - byte g = (byte)(((m / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - byte b = (byte)(((y / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - - packed.PackFromRgba32(new Rgba32(r, g, b)); - } - - /// - /// Optimized method to pack bytes to the image from the YCCK color space. - /// This is faster than implicit casting as it avoids double packing. - /// - /// The pixel format. - /// The packed pixel. - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - /// The x-position within the image. - /// The y-position within the image. - private void PackYcck(ref TPixel packed, byte y, byte cb, byte cr, int xx, int yy) - where TPixel : struct, IPixel - { - // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get - // CMY, and patch in the original K. The RGB to CMY inversion cancels - // out the 'Adobe inversion' described in the applyBlack doc comment - // above, so in practice, only the fourth channel (black) is inverted. - int ccb = cb - 128; - int ccr = cr - 128; - - // Speed up the algorithm by removing floating point calculation - // Scale by 65536, add .5F and truncate value. We use bit shifting to divide the result - int r0 = 91881 * ccr; // (1.402F * 65536) + .5F - int g0 = 22554 * ccb; // (0.34414F * 65536) + .5F - int g1 = 46802 * ccr; // (0.71414F * 65536) + .5F - int b0 = 116130 * ccb; // (1.772F * 65536) + .5F - - // First convert from YCbCr to CMY - float cyan = (y + (r0 >> 16)).Clamp(0, 255) / 255F; - float magenta = (byte)(y - (g0 >> 16) - (g1 >> 16)).Clamp(0, 255) / 255F; - float yellow = (byte)(y + (b0 >> 16)).Clamp(0, 255) / 255F; - - // Get keyline - float keyline = (255 - this.blackImage[xx, yy]) / 255F; - - // Convert back to RGB - byte r = (byte)(((1 - cyan) * (1 - keyline)).Clamp(0, 1) * 255); - byte g = (byte)(((1 - magenta) * (1 - keyline)).Clamp(0, 1) * 255); - byte b = (byte)(((1 - yellow) * (1 - keyline)).Clamp(0, 1) * 255); - - packed.PackFromRgba32(new Rgba32(r, g, b)); - } - - /// - /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters. - /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not - /// deleted by default when deleting all metadata because it may affect the appearance of the image. - /// - /// The remaining number of bytes in the stream. - private void ProcessApp14Marker(int remaining) - { - if (remaining < 12) - { - this.InputProcessor.Skip(remaining); - return; - } - - this.InputProcessor.ReadFull(this.Temp, 0, 12); - remaining -= 12; - - if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b' - && this.Temp[4] == 'e') - { - this.adobeTransformValid = true; - this.adobeTransform = this.Temp[11]; - } - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the App1 marker retrieving any stored metadata - /// - /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp1Marker(int remaining, ImageMetaData metadata) - { - if (remaining < 6 || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (profile[0] == 'E' && - profile[1] == 'x' && - profile[2] == 'i' && - profile[3] == 'f' && - profile[4] == '\0' && - profile[5] == '\0') - { - this.isExif = true; - metadata.ExifProfile = new ExifProfile(profile); - } - } - - /// - /// Processes the App2 marker retrieving any stored ICC profile information - /// - /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp2Marker(int remaining, ImageMetaData metadata) - { - // Length is 14 though we only need to check 12. - const int Icclength = 14; - if (remaining < Icclength || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] identifier = new byte[Icclength]; - this.InputProcessor.ReadFull(identifier, 0, Icclength); - remaining -= Icclength; // we have read it by this point - - if (identifier[0] == 'I' && - identifier[1] == 'C' && - identifier[2] == 'C' && - identifier[3] == '_' && - identifier[4] == 'P' && - identifier[5] == 'R' && - identifier[6] == 'O' && - identifier[7] == 'F' && - identifier[8] == 'I' && - identifier[9] == 'L' && - identifier[10] == 'E' && - identifier[11] == '\0') - { - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (metadata.IccProfile == null) - { - metadata.IccProfile = new IccProfile(profile); - } - else - { - metadata.IccProfile.Extend(profile); - } - } - else - { - // not an ICC profile we can handle read the remaining so we can carry on and ignore this. - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the application header containing the JFIF identifier plus extra data. - /// - /// The remaining bytes in the segment block. - private void ProcessApplicationHeader(int remaining) - { - if (remaining < 5) - { - this.InputProcessor.Skip(remaining); - return; - } - - this.InputProcessor.ReadFull(this.Temp, 0, 13); - remaining -= 13; - - // TODO: We should be using constants for this. - this.isJfif = this.Temp[0] == 'J' && - this.Temp[1] == 'F' && - this.Temp[2] == 'I' && - this.Temp[3] == 'F' && - this.Temp[4] == '\x00'; - - if (this.isJfif) - { - this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[8] << 8)); - this.verticalResolution = (short)(this.Temp[11] + (this.Temp[10] << 8)); - } - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes a Define Huffman Table marker, and initializes a huffman - /// struct from its contents. Specified in section B.2.4.2. - /// - /// The remaining bytes in the segment block. - private void ProcessDefineHuffmanTablesMarker(int remaining) - { - while (remaining > 0) - { - if (remaining < 17) - { - throw new ImageFormatException("DHT has wrong length"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, 17); - - int tc = this.Temp[0] >> 4; - if (tc > HuffmanTree.MaxTc) - { - throw new ImageFormatException("Bad Tc value"); - } - - int th = this.Temp[0] & 0x0f; - if (th > HuffmanTree.MaxTh || (!this.IsProgressive && (th > 1))) - { - throw new ImageFormatException("Bad Th value"); - } - - int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th; - this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( - ref this.InputProcessor, - this.Temp, - ref remaining); - } - } - - /// - /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in - /// macroblocks - /// - /// The remaining bytes in the segment block. - private void ProcessDefineRestartIntervalMarker(int remaining) - { - if (remaining != 2) - { - throw new ImageFormatException("DRI has wrong length"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; - } - - /// - /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. - /// - /// The remaining bytes in the segment block. - /// - /// Thrown if the tables do not match the header - /// - private void ProcessDqt(int remaining) - { - while (remaining > 0) - { - bool done = false; - - remaining--; - byte x = this.InputProcessor.ReadByte(); - int tq = x & 0x0F; - if (tq > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - switch (x >> 4) - { - case 0: - if (remaining < Block8x8F.ScalarCount) - { - done = true; - break; - } - - remaining -= Block8x8F.ScalarCount; - this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.ScalarCount); - - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - this.QuantizationTables[tq][i] = this.Temp[i]; - } - - break; - case 1: - if (remaining < 2 * Block8x8F.ScalarCount) - { - done = true; - break; - } - - remaining -= 2 * Block8x8F.ScalarCount; - this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount); - - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; - } - - break; - default: - throw new ImageFormatException("Bad Pq value"); - } - - if (done) - { - break; - } - } - - if (remaining != 0) - { - throw new ImageFormatException("DQT has wrong length"); - } - } - - /// - /// Processes the Start of Frame marker. Specified in section B.2.2. - /// - /// The remaining bytes in the segment block. - private void ProcessStartOfFrameMarker(int remaining) - { - if (this.ComponentCount != 0) - { - throw new ImageFormatException("Multiple SOF markers"); - } - - switch (remaining) - { - case 6 + (3 * 1): // Grayscale image. - this.ComponentCount = 1; - break; - case 6 + (3 * 3): // YCbCr or RGB image. - this.ComponentCount = 3; - break; - case 6 + (3 * 4): // YCbCrK or CMYK image. - this.ComponentCount = 4; - break; - default: - throw new ImageFormatException("Incorrect number of components"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - - // We only support 8-bit precision. - if (this.Temp[0] != 8) - { - throw new ImageFormatException("Only 8-Bit precision supported."); - } - - this.ImageHeight = (this.Temp[1] << 8) + this.Temp[2]; - this.ImageWidth = (this.Temp[3] << 8) + this.Temp[4]; - if (this.Temp[5] != this.ComponentCount) - { - throw new ImageFormatException("SOF has wrong length"); - } - - for (int i = 0; i < this.ComponentCount; i++) - { - this.ComponentArray[i].Identifier = this.Temp[6 + (3 * i)]; - - // Section B.2.2 states that "the value of C_i shall be different from - // the values of C_1 through C_(i-1)". - for (int j = 0; j < i; j++) - { - if (this.ComponentArray[i].Identifier == this.ComponentArray[j].Identifier) - { - throw new ImageFormatException("Repeated component identifier"); - } - } - - this.ComponentArray[i].Selector = this.Temp[8 + (3 * i)]; - if (this.ComponentArray[i].Selector > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - byte hv = this.Temp[7 + (3 * i)]; - int h = hv >> 4; - int v = hv & 0x0f; - if (h < 1 || h > 4 || v < 1 || v > 4) - { - throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio"); - } - - if (h == 3 || v == 3) - { - throw new ImageFormatException("Lnsupported subsampling ratio"); - } - - switch (this.ComponentCount) - { - case 1: - - // If a JPEG image has only one component, section A.2 says "this data - // is non-interleaved by definition" and section A.2.2 says "[in this - // case...] the order of data units within a scan shall be left-to-right - // and top-to-bottom... regardless of the values of H_1 and V_1". Section - // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be - // one data unit". Similarly, section A.1.1 explains that it is the ratio - // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale - // images, H_1 is the maximum H_j for all components j, so that ratio is - // always 1. The component's (h, v) is effectively always (1, 1): even if - // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 - // MCUs, not two 16x8 MCUs. - h = 1; - v = 1; - break; - - case 3: - - // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, - // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the - // (h, v) values for the Y component are either (1, 1), (1, 2), - // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values - // must be a multiple of the Cb and Cr component's values. We also - // assume that the two chroma components have the same subsampling - // ratio. - switch (i) - { - case 0: - { - // Y. - // We have already verified, above, that h and v are both - // either 1, 2 or 4, so invalid (h, v) combinations are those - // with v == 4. - if (v == 4) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 1: - { - // Cb. - if (this.ComponentArray[0].HorizontalFactor % h != 0 - || this.ComponentArray[0].VerticalFactor % v != 0) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 2: - { - // Cr. - if (this.ComponentArray[1].HorizontalFactor != h - || this.ComponentArray[1].VerticalFactor != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - } - - break; - - case 4: - - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - switch (i) - { - case 0: - if (hv != 0x11 && hv != 0x22) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 1: - case 2: - if (hv != 0x11) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 3: - if (this.ComponentArray[0].HorizontalFactor != h - || this.ComponentArray[0].VerticalFactor != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - break; - } - - this.ComponentArray[i].HorizontalFactor = h; - this.ComponentArray[i].VerticalFactor = v; - } - - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; - this.MCUCountX = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); - this.MCUCountY = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); - - // As a preparation for parallelizing Scan decoder, we also allocate DecodedBlocks in the non-progressive case! - for (int i = 0; i < this.ComponentCount; i++) - { - int count = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor - * this.ComponentArray[i].VerticalFactor; - this.DecodedBlocks[i] = Buffer.CreateClean(count); - } - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 6c6561468f..8850f581c1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Encoder for writing the data image to a stream in jpeg format. /// @@ -25,13 +21,11 @@ namespace ImageSharp.Formats /// Gets or sets the quality, that will be used to encode the image. Quality /// index must be between 0 and 100 (compression from max to min). /// - /// The quality of the jpg image from 0 to 100. - public int Quality { get; set; } + public int Quality { get; set; } = 75; /// /// Gets or sets the subsample ration, that will be used to encode the image. /// - /// The subsample ratio of the jpg image. public JpegSubsample? Subsample { get; set; } /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs index 23cd5d8752..4f368dcdee 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// @@ -19,9 +18,9 @@ namespace ImageSharp.Formats public string DefaultMimeType => "image/jpeg"; /// - public IEnumerable MimeTypes => JpegConstants.MimeTypes; + public IEnumerable MimeTypes => OrigJpegConstants.MimeTypes; /// - public IEnumerable FileExtensions => JpegConstants.FileExtensions; + public IEnumerable FileExtensions => OrigJpegConstants.FileExtensions; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs b/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs index b72b290c04..d888986f39 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Detects Jpeg file headers /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs index 287323bdd4..8558157059 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Jpeg { /// /// Enumerates the chroma subsampling method applied to the image. diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs new file mode 100644 index 0000000000..3c35e311f1 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a component block + /// + internal class PdfJsComponent : IDisposable + { +#pragma warning disable SA1401 + /// + /// Gets or sets the output + /// + public Buffer Output; + + /// + /// Gets or sets the scaling factors + /// + public Vector2 Scale; + + /// + /// Gets or sets the number of blocks per line + /// + public int BlocksPerLine; + + /// + /// Gets or sets the number of blocks per column + /// + public int BlocksPerColumn; + + /// + public void Dispose() + { + this.Output?.Dispose(); + this.Output = null; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs new file mode 100644 index 0000000000..86a0c6b317 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Contains all the decoded component blocks + /// + internal sealed class PdfJsComponentBlocks : IDisposable + { + /// + /// Gets or sets the component blocks + /// + public PdfJsComponent[] Components { get; set; } + + /// + public void Dispose() + { + if (this.Components != null) + { + for (int i = 0; i < this.Components.Length; i++) + { + this.Components[i].Dispose(); + } + + this.Components = null; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs new file mode 100644 index 0000000000..d6ff1e9eda --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a jpeg file marker + /// + internal struct PdfJsFileMarker + { + /// + /// Initializes a new instance of the struct. + /// + /// The marker + /// The position within the stream + public PdfJsFileMarker(ushort marker, long position) + { + this.Marker = marker; + this.Position = position; + this.Invalid = false; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The marker + /// The position within the stream + /// Whether the current marker is invalid + public PdfJsFileMarker(ushort marker, long position, bool invalid) + { + this.Marker = marker; + this.Position = position; + this.Invalid = invalid; + } + + /// + /// Gets or sets a value indicating whether the current marker is invalid + /// + public bool Invalid { get; set; } + + /// + /// Gets the position of the marker within a stream + /// + public ushort Marker { get; } + + /// + /// Gets the position of the marker within a stream + /// + public long Position { get; } + + /// + public override string ToString() + { + return this.Marker.ToString("X"); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs new file mode 100644 index 0000000000..8ce981a09d --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs @@ -0,0 +1,102 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represent a single jpeg frame + /// + internal sealed class PdfJsFrame : IDisposable + { + /// + /// Gets or sets a value indicating whether the frame uses the extended specification + /// + public bool Extended { get; set; } + + /// + /// Gets or sets a value indicating whether the frame uses the progressive specification + /// + public bool Progressive { get; set; } + + /// + /// Gets or sets the precision + /// + public byte Precision { get; set; } + + /// + /// Gets or sets the number of scanlines within the frame + /// + public short Scanlines { get; set; } + + /// + /// Gets or sets the number of samples per scanline + /// + public short SamplesPerLine { get; set; } + + /// + /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4 + /// + public byte ComponentCount { get; set; } + + /// + /// Gets or sets the component id collection + /// + public byte[] ComponentIds { get; set; } + + /// + /// Gets or sets the frame component collection + /// + public PdfJsFrameComponent[] Components { get; set; } + + /// + /// Gets or sets the maximum horizontal sampling factor + /// + public int MaxHorizontalFactor { get; set; } + + /// + /// Gets or sets the maximum vertical sampling factor + /// + public int MaxVerticalFactor { get; set; } + + /// + /// Gets or sets the number of MCU's per line + /// + public int McusPerLine { get; set; } + + /// + /// Gets or sets the number of MCU's per column + /// + public int McusPerColumn { get; set; } + + /// + public void Dispose() + { + if (this.Components != null) + { + for (int i = 0; i < this.Components.Length; i++) + { + this.Components[i].Dispose(); + } + + this.Components = null; + } + } + + /// + /// Allocates the frame component blocks + /// + public void InitComponents() + { + this.McusPerLine = (int)MathF.Ceiling(this.SamplesPerLine / 8F / this.MaxHorizontalFactor); + this.McusPerColumn = (int)MathF.Ceiling(this.Scanlines / 8F / this.MaxVerticalFactor); + + for (int i = 0; i < this.ComponentCount; i++) + { + PdfJsFrameComponent component = this.Components[i]; + component.Init(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs new file mode 100644 index 0000000000..f60097dc9c --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs @@ -0,0 +1,132 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a single frame component + /// + internal class PdfJsFrameComponent : IDisposable, IJpegComponent + { +#pragma warning disable SA1401 // Fields should be private + + public PdfJsFrameComponent(PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) + { + this.Frame = frame; + this.Id = id; + this.HorizontalSamplingFactor = horizontalFactor; + this.VerticalSamplingFactor = verticalFactor; + this.QuantizationTableIndex = quantizationTableIndex; + this.Index = index; + } + + /// + /// Gets the component Id + /// + public byte Id { get; } + + /// + /// Gets or sets Pred TODO: What does pred stand for? + /// + public int Pred { get; set; } + + /// + /// Gets the horizontal sampling factor. + /// + public int HorizontalSamplingFactor { get; } + + /// + /// Gets the vertical sampling factor. + /// + public int VerticalSamplingFactor { get; } + + Buffer2D IJpegComponent.SpectralBlocks => throw new NotImplementedException(); + + // TODO: Should be derived from PdfJsComponent.Scale + public Size SubSamplingDivisors => throw new NotImplementedException(); + + /// + public int QuantizationTableIndex { get; } + + /// + /// Gets the block data + /// + public Buffer BlockData { get; private set; } + + /// + public int Index { get; } + + public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks); + + public Size SamplingFactors => new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor); + + /// + /// Gets the number of blocks per line + /// + public int WidthInBlocks { get; private set; } + + /// + /// Gets the number of blocks per column + /// + public int HeightInBlocks { get; private set; } + + /// + /// Gets or sets the index for the DC Huffman table + /// + public int DCHuffmanTableId { get; set; } + + /// + /// Gets or sets the index for the AC Huffman table + /// + public int ACHuffmanTableId { get; set; } + + internal int BlocksPerLineForMcu { get; private set; } + + internal int BlocksPerColumnForMcu { get; private set; } + + public PdfJsFrame Frame { get; } + + /// + public void Dispose() + { + this.BlockData?.Dispose(); + this.BlockData = null; + } + + public void Init() + { + this.WidthInBlocks = (int)MathF.Ceiling( + MathF.Ceiling(this.Frame.SamplesPerLine / 8F) * this.HorizontalSamplingFactor / this.Frame.MaxHorizontalFactor); + + this.HeightInBlocks = (int)MathF.Ceiling( + MathF.Ceiling(this.Frame.Scanlines / 8F) * this.VerticalSamplingFactor / this.Frame.MaxVerticalFactor); + + this.BlocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor; + this.BlocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor; + + int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1); + + // Pooled. Disposed via frame disposal + this.BlockData = Buffer.CreateClean(blocksBufferSize); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetBlockBufferOffset(int row, int col) + { + return 64 * (((this.WidthInBlocks + 1) * row) + col); + } + + public Span GetBlockBuffer(int row, int col) + { + int offset = this.GetBlockBufferOffset(row, col); + return this.BlockData.Span.Slice(offset, 64); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs new file mode 100644 index 0000000000..9dc8315677 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -0,0 +1,209 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a Huffman Table + /// + internal struct PdfJsHuffmanTable : IDisposable + { + private Buffer lookahead; + private Buffer valOffset; + private Buffer maxcode; + private Buffer huffval; + + /// + /// Initializes a new instance of the struct. + /// + /// The code lengths + /// The huffman values + public PdfJsHuffmanTable(byte[] lengths, byte[] values) + { + this.lookahead = Buffer.CreateClean(256); + this.valOffset = Buffer.CreateClean(18); + this.maxcode = Buffer.CreateClean(18); + + using (var huffsize = Buffer.CreateClean(257)) + using (var huffcode = Buffer.CreateClean(257)) + { + GenerateSizeTable(lengths, huffsize); + GenerateCodeTable(huffsize, huffcode); + GenerateDecoderTables(lengths, huffcode, this.valOffset, this.maxcode); + GenerateLookaheadTables(lengths, values, this.lookahead); + } + + this.huffval = Buffer.CreateClean(values.Length); + Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); + + this.MaxCode = this.maxcode.Array; + this.ValOffset = this.valOffset.Array; + this.HuffVal = this.huffval.Array; + this.Lookahead = this.lookahead.Array; + } + + /// + /// Gets the max code array + /// + public long[] MaxCode + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the value offset array + /// + public short[] ValOffset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the huffman value array + /// + public byte[] HuffVal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the lookahead array + /// + public short[] Lookahead + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + public void Dispose() + { + this.lookahead?.Dispose(); + this.valOffset?.Dispose(); + this.maxcode?.Dispose(); + this.huffval?.Dispose(); + + this.lookahead = null; + this.valOffset = null; + this.maxcode = null; + this.huffval = null; + } + + /// + /// Figure C.1: make table of Huffman code length for each symbol + /// + /// The code lengths + /// The huffman size span + private static void GenerateSizeTable(byte[] lengths, Span huffsize) + { + short index = 0; + for (short l = 1; l <= 16; l++) + { + byte i = lengths[l]; + for (short j = 0; j < i; j++) + { + huffsize[index] = l; + index++; + } + } + + huffsize[index] = 0; + } + + /// + /// Figure C.2: generate the codes themselves + /// + /// The huffman size span + /// The huffman code span + private static void GenerateCodeTable(Span huffsize, Span huffcode) + { + short k = 0; + short si = huffsize[0]; + short code = 0; + for (short i = 0; i < huffsize.Length; i++) + { + while (huffsize[k] == si) + { + huffcode[k] = code; + code++; + k++; + } + + code <<= 1; + si++; + } + } + + /// + /// Figure F.15: generate decoding tables for bit-sequential decoding + /// + /// The code lengths + /// The huffman code span + /// The value offset span + /// The max code span + private static void GenerateDecoderTables(byte[] lengths, Span huffcode, Span valOffset, Span maxcode) + { + short bitcount = 0; + for (int i = 1; i <= 16; i++) + { + if (lengths[i] != 0) + { + // valoffset[l] = huffval[] index of 1st symbol of code length i, + // minus the minimum code of length i + valOffset[i] = (short)(bitcount - huffcode[bitcount]); + bitcount += lengths[i]; + maxcode[i] = huffcode[bitcount - 1]; // maximum code of length i + } + else + { + maxcode[i] = -1; // -1 if no codes of this length + } + } + + valOffset[17] = 0; + maxcode[17] = 0xFFFFFL; + } + + /// + /// Generates lookup tables to speed up decoding + /// + /// The code lengths + /// The huffman value array + /// The lookahead span + private static void GenerateLookaheadTables(byte[] lengths, byte[] huffval, Span lookahead) + { + int x = 0, code = 0; + + for (int i = 0; i < 8; i++) + { + code <<= 1; + + for (int j = 0; j < lengths[i + 1]; j++) + { + // The codeLength is 1+i, so shift code by 8-(1+i) to + // calculate the high bits for every 8-bit sequence + // whose codeLength's high bits matches code. + // The high 8 bits of lutValue are the encoded value. + // The low 8 bits are 1 plus the codeLength. + byte base2 = (byte)(code << (7 - i)); + short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i)); + + for (int k = 0; k < 1 << (7 - i); k++) + { + lookahead[base2 | k] = lutValue; + } + + code++; + x++; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs new file mode 100644 index 0000000000..5d59809cc7 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Defines a pair of huffman tables + /// + internal sealed class PdfJsHuffmanTables : IDisposable + { + private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4]; + + /// + /// Gets or sets the table at the given index. + /// + /// The index + /// The + public ref PdfJsHuffmanTable this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref this.tables[index]; + } + } + + /// + public void Dispose() + { + for (int i = 0; i < this.tables.Length; i++) + { + this.tables[i].Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs new file mode 100644 index 0000000000..49bdc2423e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs @@ -0,0 +1,512 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Performs the inverse Descrete Cosine Transform on each frame component. + /// + internal static class PdfJsIDCT + { + /// + /// Precomputed values scaled up by 14 bits + /// + public static readonly short[] Aanscales = + { + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, + 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, + 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, + 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, + 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, + 4520, 3552, 2446, 1247 + }; + + private const int DctCos1 = 4017; // cos(pi/16) + private const int DctSin1 = 799; // sin(pi/16) + private const int DctCos3 = 3406; // cos(3*pi/16) + private const int DctSin3 = 2276; // sin(3*pi/16) + private const int DctCos6 = 1567; // cos(6*pi/16) + private const int DctSin6 = 3784; // sin(6*pi/16) + private const int DctSqrt2 = 5793; // sqrt(2) + private const int DctSqrt1D2 = 2896; // sqrt(2) / 2 + +#pragma warning disable SA1310 // Field names must not contain underscore + private const int FIX_1_082392200 = 277; // FIX(1.082392200) + private const int FIX_1_414213562 = 362; // FIX(1.414213562) + private const int FIX_1_847759065 = 473; // FIX(1.847759065) + private const int FIX_2_613125930 = 669; // FIX(2.613125930) +#pragma warning restore SA1310 // Field names must not contain underscore + + private const int ConstBits = 8; + private const int Pass1Bits = 2; // Factional bits in scale factors + private const int MaxJSample = 255; + private const int CenterJSample = 128; + private const int RangeCenter = (MaxJSample * 2) + 2; + + // First segment of range limit table: limit[x] = 0 for x < 0 + // allow negative subscripts of simple table + private const int TableOffset = 2 * (MaxJSample + 1); + private const int LimitOffset = TableOffset - (RangeCenter - CenterJSample); + + // Each IDCT routine is responsible for range-limiting its results and + // converting them to unsigned form (0..MaxJSample). The raw outputs could + // be quite far out of range if the input data is corrupt, so a bulletproof + // range-limiting step is required. We use a mask-and-table-lookup method + // to do the combined operations quickly, assuming that MaxJSample+1 + // is a power of 2. + private const int RangeMask = (MaxJSample * 4) + 3; // 2 bits wider than legal samples + + private static readonly byte[] Limit = new byte[5 * (MaxJSample + 1)]; + + static PdfJsIDCT() + { + // Main part of range limit table: limit[x] = x + int i; + for (i = 0; i <= MaxJSample; i++) + { + Limit[TableOffset + i] = (byte)i; + } + + // End of range limit table: Limit[x] = MaxJSample for x > MaxJSample + for (; i < 3 * (MaxJSample + 1); i++) + { + Limit[TableOffset + i] = MaxJSample; + } + } + + /// + /// A port of Poppler's IDCT method which in turn is taken from: + /// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, + /// 'Practical Fast 1-D DCT Algorithms with 11 Multiplications', + /// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991. + /// + /// The fram component + /// The block buffer offset + /// The computational buffer for holding temp values + /// The quantization table + public static void QuantizeAndInverse(PdfJsFrameComponent component, int blockBufferOffset, ref Span computationBuffer, ref Span quantizationTable) + { + Span blockData = component.BlockData.Slice(blockBufferOffset); + int v0, v1, v2, v3, v4, v5, v6, v7; + int p0, p1, p2, p3, p4, p5, p6, p7; + int t; + + // inverse DCT on rows + for (int row = 0; row < 64; row += 8) + { + // gather block data + p0 = blockData[row]; + p1 = blockData[row + 1]; + p2 = blockData[row + 2]; + p3 = blockData[row + 3]; + p4 = blockData[row + 4]; + p5 = blockData[row + 5]; + p6 = blockData[row + 6]; + p7 = blockData[row + 7]; + + // dequant p0 + p0 *= quantizationTable[row]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + t = ((DctSqrt2 * p0) + 512) >> 10; + short st = (short)t; + computationBuffer[row] = st; + computationBuffer[row + 1] = st; + computationBuffer[row + 2] = st; + computationBuffer[row + 3] = st; + computationBuffer[row + 4] = st; + computationBuffer[row + 5] = st; + computationBuffer[row + 6] = st; + computationBuffer[row + 7] = st; + continue; + } + + // dequant p1 ... p7 + p1 *= quantizationTable[row + 1]; + p2 *= quantizationTable[row + 2]; + p3 *= quantizationTable[row + 3]; + p4 *= quantizationTable[row + 4]; + p5 *= quantizationTable[row + 5]; + p6 *= quantizationTable[row + 6]; + p7 *= quantizationTable[row + 7]; + + // stage 4 + v0 = ((DctSqrt2 * p0) + 128) >> 8; + v1 = ((DctSqrt2 * p4) + 128) >> 8; + v2 = p2; + v3 = p6; + v4 = ((DctSqrt1D2 * (p1 - p7)) + 128) >> 8; + v7 = ((DctSqrt1D2 * (p1 + p7)) + 128) >> 8; + v5 = p3 << 4; + v6 = p5 << 4; + + // stage 3 + v0 = (v0 + v1 + 1) >> 1; + v1 = v0 - v1; + t = ((v2 * DctSin6) + (v3 * DctCos6) + 128) >> 8; + v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 128) >> 8; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; + v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; + v7 = t; + t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; + v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; + v6 = t; + + // stage 1 + computationBuffer[row] = (short)(v0 + v7); + computationBuffer[row + 7] = (short)(v0 - v7); + computationBuffer[row + 1] = (short)(v1 + v6); + computationBuffer[row + 6] = (short)(v1 - v6); + computationBuffer[row + 2] = (short)(v2 + v5); + computationBuffer[row + 5] = (short)(v2 - v5); + computationBuffer[row + 3] = (short)(v3 + v4); + computationBuffer[row + 4] = (short)(v3 - v4); + } + + // inverse DCT on columns + for (int col = 0; col < 8; ++col) + { + p0 = computationBuffer[col]; + p1 = computationBuffer[col + 8]; + p2 = computationBuffer[col + 16]; + p3 = computationBuffer[col + 24]; + p4 = computationBuffer[col + 32]; + p5 = computationBuffer[col + 40]; + p6 = computationBuffer[col + 48]; + p7 = computationBuffer[col + 56]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + t = ((DctSqrt2 * p0) + 8192) >> 14; + + // convert to 8 bit + t = (t < -2040) ? 0 : (t >= 2024) ? MaxJSample : (t + 2056) >> 4; + short st = (short)t; + + blockData[col] = st; + blockData[col + 8] = st; + blockData[col + 16] = st; + blockData[col + 24] = st; + blockData[col + 32] = st; + blockData[col + 40] = st; + blockData[col + 48] = st; + blockData[col + 56] = st; + continue; + } + + // stage 4 + v0 = ((DctSqrt2 * p0) + 2048) >> 12; + v1 = ((DctSqrt2 * p4) + 2048) >> 12; + v2 = p2; + v3 = p6; + v4 = ((DctSqrt1D2 * (p1 - p7)) + 2048) >> 12; + v7 = ((DctSqrt1D2 * (p1 + p7)) + 2048) >> 12; + v5 = p3; + v6 = p5; + + // stage 3 + // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when + // converting to UInt8 range later. + v0 = ((v0 + v1 + 1) >> 1) + 4112; + v1 = v0 - v1; + t = ((v2 * DctSin6) + (v3 * DctCos6) + 2048) >> 12; + v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 2048) >> 12; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; + v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; + v7 = t; + t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; + v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; + v6 = t; + + // stage 1 + p0 = v0 + v7; + p7 = v0 - v7; + p1 = v1 + v6; + p6 = v1 - v6; + p2 = v2 + v5; + p5 = v2 - v5; + p3 = v3 + v4; + p4 = v3 - v4; + + // convert to 8-bit integers + p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? MaxJSample : p0 >> 4; + p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? MaxJSample : p1 >> 4; + p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? MaxJSample : p2 >> 4; + p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? MaxJSample : p3 >> 4; + p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? MaxJSample : p4 >> 4; + p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? MaxJSample : p5 >> 4; + p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? MaxJSample : p6 >> 4; + p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? MaxJSample : p7 >> 4; + + // store block data + blockData[col] = (short)p0; + blockData[col + 8] = (short)p1; + blockData[col + 16] = (short)p2; + blockData[col + 24] = (short)p3; + blockData[col + 32] = (short)p4; + blockData[col + 40] = (short)p5; + blockData[col + 48] = (short)p6; + blockData[col + 56] = (short)p7; + } + } + + /// + /// A port of + /// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + /// on each row(or vice versa, but it's more convenient to emit a row at + /// a time). Direct algorithms are also available, but they are much more + /// complex and seem not to be any faster when reduced to code. + /// + /// This implementation is based on Arai, Agui, and Nakajima's algorithm for + /// scaled DCT.Their original paper (Trans.IEICE E-71(11):1095) is in + /// Japanese, but the algorithm is described in the Pennebaker & Mitchell + /// JPEG textbook(see REFERENCES section in file README.ijg). The following + /// code is based directly on figure 4-8 in P&M. + /// While an 8-point DCT cannot be done in less than 11 multiplies, it is + /// possible to arrange the computation so that many of the multiplies are + /// simple scalings of the final outputs.These multiplies can then be + /// folded into the multiplications or divisions by the JPEG quantization + /// table entries. The AA&N method leaves only 5 multiplies and 29 adds + /// to be done in the DCT itself. + /// The primary disadvantage of this method is that with fixed-point math, + /// accuracy is lost due to imprecise representation of the scaled + /// quantization values.The smaller the quantization table entry, the less + /// precise the scaled value, so this implementation does worse with high - + /// quality - setting files than with low - quality ones. + /// + /// The frame component + /// The block buffer offset + /// The computational buffer for holding temp values + /// The multiplier table + public static void QuantizeAndInverseFast(PdfJsFrameComponent component, int blockBufferOffset, ref Span computationBuffer, ref Span multiplierTable) + { + Span blockData = component.BlockData.Slice(blockBufferOffset); + int p0, p1, p2, p3, p4, p5, p6, p7; + + for (int col = 0; col < 8; col++) + { + // Gather block data + p0 = blockData[col]; + p1 = blockData[col + 8]; + p2 = blockData[col + 16]; + p3 = blockData[col + 24]; + p4 = blockData[col + 32]; + p5 = blockData[col + 40]; + p6 = blockData[col + 48]; + p7 = blockData[col + 56]; + + int tmp0 = p0 * multiplierTable[col]; + + // Due to quantization, we will usually find that many of the input + // coefficients are zero, especially the AC terms. We can exploit this + // by short-circuiting the IDCT calculation for any column in which all + // the AC terms are zero. In that case each output is equal to the + // DC coefficient (with scale factor as needed). + // With typical images and quantization tables, half or more of the + // column DCT calculations can be simplified this way. + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + short dcval = (short)tmp0; + + computationBuffer[col] = dcval; + computationBuffer[col + 8] = dcval; + computationBuffer[col + 16] = dcval; + computationBuffer[col + 24] = dcval; + computationBuffer[col + 32] = dcval; + computationBuffer[col + 40] = dcval; + computationBuffer[col + 48] = dcval; + computationBuffer[col + 56] = dcval; + + continue; + } + + // Even part + int tmp1 = p2 * multiplierTable[col + 16]; + int tmp2 = p4 * multiplierTable[col + 32]; + int tmp3 = p6 * multiplierTable[col + 48]; + + int tmp10 = tmp0 + tmp2; // Phase 3 + int tmp11 = tmp0 - tmp2; + + int tmp13 = tmp1 + tmp3; // Phases 5-3 + int tmp12 = Multiply(tmp1 - tmp3, FIX_1_414213562) - tmp13; // 2*c4 + + tmp0 = tmp10 + tmp13; // Phase 2 + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + // Odd Part + int tmp4 = p1 * multiplierTable[col + 8]; + int tmp5 = p3 * multiplierTable[col + 24]; + int tmp6 = p5 * multiplierTable[col + 40]; + int tmp7 = p7 * multiplierTable[col + 56]; + + int z13 = tmp6 + tmp5; // Phase 6 + int z10 = tmp6 - tmp5; + int z11 = tmp4 + tmp7; + int z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; // Phase 5 + tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4 + + int z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2 + tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6) + tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6) + + tmp6 = tmp12 - tmp7; // Phase 2 + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 - tmp5; + + computationBuffer[col] = (short)(tmp0 + tmp7); + computationBuffer[col + 56] = (short)(tmp0 - tmp7); + computationBuffer[col + 8] = (short)(tmp1 + tmp6); + computationBuffer[col + 48] = (short)(tmp1 - tmp6); + computationBuffer[col + 16] = (short)(tmp2 + tmp5); + computationBuffer[col + 40] = (short)(tmp2 - tmp5); + computationBuffer[col + 24] = (short)(tmp3 + tmp4); + computationBuffer[col + 32] = (short)(tmp3 - tmp4); + } + + // Pass 2: process rows from work array, store into output array. + // Note that we must descale the results by a factor of 8 == 2**3, + // and also undo the pass 1 bits scaling. + for (int row = 0; row < 64; row += 8) + { + p1 = computationBuffer[row + 1]; + p2 = computationBuffer[row + 2]; + p3 = computationBuffer[row + 3]; + p4 = computationBuffer[row + 4]; + p5 = computationBuffer[row + 5]; + p6 = computationBuffer[row + 6]; + p7 = computationBuffer[row + 7]; + + // Add range center and fudge factor for final descale and range-limit. + int z5 = computationBuffer[row] + (RangeCenter << (Pass1Bits + 3)) + (1 << (Pass1Bits + 2)); + + // Check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + byte dcval = Limit[LimitOffset + (RightShift(z5, Pass1Bits + 3) & RangeMask)]; + + blockData[row] = dcval; + blockData[row + 1] = dcval; + blockData[row + 2] = dcval; + blockData[row + 3] = dcval; + blockData[row + 4] = dcval; + blockData[row + 5] = dcval; + blockData[row + 6] = dcval; + blockData[row + 7] = dcval; + + continue; + } + + // Even part + int tmp10 = z5 + p4; + int tmp11 = z5 - p4; + + int tmp13 = p2 + p6; + int tmp12 = Multiply(p2 - p6, FIX_1_414213562) - tmp13; // 2*c4 + + int tmp0 = tmp10 + tmp13; + int tmp3 = tmp10 - tmp13; + int tmp1 = tmp11 + tmp12; + int tmp2 = tmp11 - tmp12; + + // Odd part + int z13 = p5 + p3; + int z10 = p5 - p3; + int z11 = p1 + p7; + int z12 = p1 - p7; + + int tmp7 = z11 + z13; // Phase 5 + tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4 + + z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2 + tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6) + tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6) + + int tmp6 = tmp12 - tmp7; // Phase 2 + int tmp5 = tmp11 - tmp6; + int tmp4 = tmp10 - tmp5; + + // Final output stage: scale down by a factor of 8, offset, and range-limit + blockData[row] = Limit[LimitOffset + (RightShift(tmp0 + tmp7, Pass1Bits + 3) & RangeMask)]; + blockData[row + 7] = Limit[LimitOffset + (RightShift(tmp0 - tmp7, Pass1Bits + 3) & RangeMask)]; + blockData[row + 1] = Limit[LimitOffset + (RightShift(tmp1 + tmp6, Pass1Bits + 3) & RangeMask)]; + blockData[row + 6] = Limit[LimitOffset + (RightShift(tmp1 - tmp6, Pass1Bits + 3) & RangeMask)]; + blockData[row + 2] = Limit[LimitOffset + (RightShift(tmp2 + tmp5, Pass1Bits + 3) & RangeMask)]; + blockData[row + 5] = Limit[LimitOffset + (RightShift(tmp2 - tmp5, Pass1Bits + 3) & RangeMask)]; + blockData[row + 3] = Limit[LimitOffset + (RightShift(tmp3 + tmp4, Pass1Bits + 3) & RangeMask)]; + blockData[row + 4] = Limit[LimitOffset + (RightShift(tmp3 - tmp4, Pass1Bits + 3) & RangeMask)]; + } + } + + /// + /// Descale and correctly round an int value that's scaled by bits. + /// We assume rounds towards minus infinity, so adding + /// the fudge factor is correct for either sign of . + /// + /// The value + /// The number of bits + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Descale(int value, int n) + { + return RightShift(value + (1 << (n - 1)), n); + } + + /// + /// Multiply a variable by an int constant, and immediately descale. + /// + /// The value + /// The multiplier + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Multiply(int val, int c) + { + return Descale(val * c, ConstBits); + } + + /// + /// Right-shifts the value by the given amount + /// + /// The value + /// The amount to shift by + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RightShift(int value, int shift) + { + return value >> shift; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs new file mode 100644 index 0000000000..034986c2cb --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -0,0 +1,145 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a section of the jpeg component data laid out in pixel order. + /// + internal struct PdfJsJpegPixelArea : IDisposable + { + private readonly int imageWidth; + + private readonly int imageHeight; + + private Buffer componentData; + + private int rowStride; + + /// + /// Initializes a new instance of the struct. + /// + /// The image width + /// The image height + /// The number of components + public PdfJsJpegPixelArea(int imageWidth, int imageHeight, int numberOfComponents) + { + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.Width = 0; + this.Height = 0; + this.NumberOfComponents = numberOfComponents; + this.componentData = null; + this.rowStride = 0; + } + + /// + /// Gets the number of components + /// + public int NumberOfComponents { get; } + + /// + /// Gets the width + /// + public int Width { get; private set; } + + /// + /// Gets the height + /// + public int Height { get; private set; } + + /// + /// Organsizes the decoded jpeg components into a linear array ordered by component. + /// This must be called before attempting to retrieve the data. + /// + /// The jpeg component blocks + /// The pixel area width + /// The pixel area height + public void LinearizeBlockData(PdfJsComponentBlocks components, int width, int height) + { + this.Width = width; + this.Height = height; + int numberOfComponents = this.NumberOfComponents; + this.rowStride = width * numberOfComponents; + var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); + + this.componentData = new Buffer(width * height * numberOfComponents); + Span componentDataSpan = this.componentData; + const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs + + using (var xScaleBlockOffset = new Buffer(width)) + { + Span xScaleBlockOffsetSpan = xScaleBlockOffset; + for (int i = 0; i < numberOfComponents; i++) + { + ref PdfJsComponent component = ref components.Components[i]; + Vector2 componentScale = component.Scale * scale; + int offset = i; + Span output = component.Output; + int blocksPerScanline = (component.BlocksPerLine + 1) << 3; + + // Precalculate the xScaleBlockOffset + int j; + for (int x = 0; x < width; x++) + { + j = (int)(x * componentScale.X); + xScaleBlockOffsetSpan[x] = (int)((j & Mask3Lsb) << 3) | (j & 7); + } + + // Linearize the blocks of the component + for (int y = 0; y < height; y++) + { + j = (int)(y * componentScale.Y); + int index = blocksPerScanline * (int)(j & Mask3Lsb) | ((j & 7) << 3); + for (int x = 0; x < width; x++) + { + componentDataSpan[offset] = (byte)output[index + xScaleBlockOffsetSpan[x]]; + offset += numberOfComponents; + } + } + } + } + } + + /// + /// Gets a representing the row 'y' beginning from the the first byte on that row. + /// + /// The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the pixel area. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int y) + { + this.CheckCoordinates(y); + return this.componentData.Slice(y * this.rowStride, this.rowStride); + } + + /// + public void Dispose() + { + this.componentData?.Dispose(); + this.componentData = null; + } + + /// + /// Checks the coordinates to ensure they are within bounds. + /// + /// The y-coordinate of the row. Must be greater than zero and less than the height of the area. + /// + /// Thrown if the coordinates are not within the bounds of the image. + /// + [Conditional("DEBUG")] + private void CheckCoordinates(int y) + { + if (y < 0 || y >= this.Height) + { + throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the area bounds."); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs new file mode 100644 index 0000000000..1000ce82c5 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Contains the quantization tables. + /// + internal sealed class PdfJsQuantizationTables : IDisposable + { + /// + /// Gets the ZigZag scan table + /// + public static byte[] DctZigZag + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + = + { + 0, + 1, 8, + 16, 9, 2, + 3, 10, 17, 24, + 32, 25, 18, 11, 4, + 5, 12, 19, 26, 33, 40, + 48, 41, 34, 27, 20, 13, 6, + 7, 14, 21, 28, 35, 42, 49, 56, + 57, 50, 43, 36, 29, 22, 15, + 23, 30, 37, 44, 51, 58, + 59, 52, 45, 38, 31, + 39, 46, 53, 60, + 61, 54, 47, + 55, 62, + 63 + }; + + /// + /// Gets or sets the quantization tables. + /// + public Buffer2D Tables + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; set; + } + + = new Buffer2D(64, 4); + + /// + public void Dispose() + { + if (this.Tables != null) + { + this.Tables.Dispose(); + this.Tables = null; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs new file mode 100644 index 0000000000..e2e5d985e6 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs @@ -0,0 +1,937 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Provides the means to decode a spectral scan + /// + internal struct PdfJsScanDecoder + { + private byte[] markerBuffer; + + private int bitsData; + + private int bitsCount; + +#pragma warning disable 414 + private int bitsUnRead; + + private int accumulator; +#pragma warning restore 414 + + private int specStart; + + private int specEnd; + + private int eobrun; + + private int compIndex; + + private int successiveState; + + private int successiveACState; + + private int successiveACNextValue; + + private bool endOfStreamReached; + + private bool unexpectedMarkerReached; + + /// + /// Decodes the spectral scan + /// + /// The image frame + /// The input stream + /// The DC Huffman tables + /// The AC Huffman tables + /// The scan components + /// The component index within the array + /// The length of the components. Different to the array length + /// The reset interval + /// The spectral selection start + /// The spectral selection end + /// The successive approximation bit high end + /// The successive approximation bit low end + public void DecodeScan( + PdfJsFrame frame, + Stream stream, + PdfJsHuffmanTables dcHuffmanTables, + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentIndex, + int componentsLength, + ushort resetInterval, + int spectralStart, + int spectralEnd, + int successivePrev, + int successive) + { + this.markerBuffer = new byte[2]; + this.compIndex = componentIndex; + this.specStart = spectralStart; + this.specEnd = spectralEnd; + this.successiveState = successive; + this.endOfStreamReached = false; + this.unexpectedMarkerReached = false; + + bool progressive = frame.Progressive; + int mcusPerLine = frame.McusPerLine; + + int mcu = 0; + int mcuExpected; + if (componentsLength == 1) + { + mcuExpected = components[this.compIndex].WidthInBlocks * components[this.compIndex].HeightInBlocks; + } + else + { + mcuExpected = mcusPerLine * frame.McusPerColumn; + } + + PdfJsFileMarker fileMarker; + while (mcu < mcuExpected) + { + // Reset interval stuff + int mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - mcu, resetInterval) : mcuExpected; + for (int i = 0; i < components.Length; i++) + { + PdfJsFrameComponent c = components[i]; + c.Pred = 0; + } + + this.eobrun = 0; + + if (!progressive) + { + this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + else + { + if (this.specStart == 0) + { + if (successivePrev == 0) + { + this.DecodeScanDCFirst(dcHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + else + { + this.DecodeScanDCSuccessive(components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + } + else + { + if (successivePrev == 0) + { + this.DecodeScanACFirst(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + else + { + this.DecodeScanACSuccessive(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + } + } + + // Find marker + this.bitsCount = 0; + this.accumulator = 0; + this.bitsUnRead = 0; + fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); + + // Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past + // those to attempt to find a valid marker (fixes issue4090.pdf) in original code. + if (fileMarker.Invalid) + { +#if DEBUG + Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); +#endif + } + + ushort marker = fileMarker.Marker; + + // RSTn - We've alread read the bytes and altered the position so no need to skip + if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) + { + continue; + } + + if (!fileMarker.Invalid) + { + // We've found a valid marker. + // Rewind the stream to the position of the marker and break + stream.Position = fileMarker.Position; + break; + } + } + + fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); + + // Some images include more Scan blocks than expected, skip past those and + // attempt to find the next valid marker (fixes issue8182.pdf) in original code. + if (fileMarker.Invalid) + { +#if DEBUG + Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); +#endif + } + else + { + // We've found a valid marker. + // Rewind the stream to the position of the marker + stream.Position = fileMarker.Position; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanBaseline( + PdfJsHuffmanTables dcHuffmanTables, + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanDCFirst( + PdfJsHuffmanTables dcHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockDCFirst(ref dcHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuDCFirst(ref dcHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanDCSuccessive( + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockDCSuccessive(component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuDCSuccessive(component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanACFirst( + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockACFirst(ref acHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuACFirst(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanACSuccessive( + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockACSuccessive(ref acHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuACSuccessive(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCSuccessive(component, offset, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCSuccessive(component, offset, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACFirst(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACFirst(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ReadBit(Stream stream) + { + // TODO: I wonder if we can do this two bytes at a time; libjpeg turbo seems to do that? + if (this.bitsCount > 0) + { + this.bitsCount--; + return (this.bitsData >> this.bitsCount) & 1; + } + + this.bitsData = stream.ReadByte(); + + if (this.bitsData == -0x1) + { + // We've encountered the end of the file stream which means there's no EOI marker in the image + this.endOfStreamReached = true; + } + + if (this.bitsData == PdfJsJpegConstants.Markers.Prefix) + { + int nextByte = stream.ReadByte(); + if (nextByte != 0) + { +#if DEBUG + Debug.WriteLine($"DecodeScan - Unexpected marker {(this.bitsData << 8) | nextByte:X} at {stream.Position}"); +#endif + + // We've encountered an unexpected marker. Reverse the stream and exit. + this.unexpectedMarkerReached = true; + stream.Position -= 2; + } + + // Unstuff 0 + } + + this.bitsCount = 7; + + return this.bitsData >> 7; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private short DecodeHuffman(ref PdfJsHuffmanTable tree, Stream stream) + { + short code = -1; + + // TODO: Adding this code introduces error into the decoder. + // NOTES # During investigation of the libjpeg implementation it appears that they pull 32bits at a time and operate on those bits + // using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same. + // It doesn't appear to speed anything up either. + // if (this.bitsUnRead < 8) + // { + // if (this.bitsCount <= 0) + // { + // code = (short)this.ReadBit(stream); + // if (this.endOfStreamReached || this.unexpectedMarkerReached) + // { + // return -1; + // } + // + // this.bitsUnRead += 8; + // } + // + // this.accumulator = (this.accumulator << 8) | this.bitsData; + // int lutIndex = (this.accumulator >> (8 - this.bitsUnRead)) & 0xFF; + // int v = tree.Lookahead[lutIndex]; + // if (v != 0) + // { + // int nb = (v & 0xFF) - 1; + // this.bitsCount -= nb - 1; + // this.bitsUnRead -= nb; + // v = v >> 8; + // return (short)v; + // } + // } + if (code == -1) + { + code = (short)this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return -1; + } + } + + // "DECODE", section F.2.2.3, figure F.16, page 109 of T.81 + int i = 1; + + while (code > tree.MaxCode[i]) + { + code <<= 1; + code |= (short)this.ReadBit(stream); + + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return -1; + } + + i++; + } + + int j = tree.ValOffset[i]; + return tree.HuffVal[(j + code) & 0xFF]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int Receive(int length, Stream stream) + { + int n = 0; + while (length > 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return -1; + } + + n = (n << 1) | bit; + length--; + } + + return n; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ReceiveAndExtend(int length, Stream stream) + { + if (length == 1) + { + return this.ReadBit(stream) == 1 ? 1 : -1; + } + + int n = this.Receive(length, stream); + if (n >= 1 << (length - 1)) + { + return n; + } + + return n + (-1 << length) + 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + { + int t = this.DecodeHuffman(ref dcHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream); + component.BlockData[offset] = (short)(component.Pred += diff); + + int k = 1; + while (k < 64) + { + int rs = this.DecodeHuffman(ref acHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int s = rs & 15; + int r = rs >> 4; + + if (s == 0) + { + if (r < 15) + { + break; + } + + k += 16; + continue; + } + + k += r; + + if (k > 63) + { + break; + } + + byte z = PdfJsQuantizationTables.DctZigZag[k]; + short re = (short)this.ReceiveAndExtend(s, stream); + component.BlockData[offset + z] = re; + k++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) + { + int t = this.DecodeHuffman(ref dcHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState; + component.BlockData[offset] = (short)(component.Pred += diff); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + component.BlockData[offset] |= (short)(bit << this.successiveState); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeACFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + { + if (this.eobrun > 0) + { + this.eobrun--; + return; + } + + Span componentBlockDataSpan = component.BlockData.Span; + int k = this.specStart; + int e = this.specEnd; + while (k <= e) + { + short rs = this.DecodeHuffman(ref acHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int s = rs & 15; + int r = rs >> 4; + + if (s == 0) + { + if (r < 15) + { + this.eobrun = this.Receive(r, stream) + (1 << r) - 1; + break; + } + + k += 16; + continue; + } + + k += r; + byte z = PdfJsQuantizationTables.DctZigZag[k]; + componentBlockDataSpan[offset + z] = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState)); + k++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeACSuccessive(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + { + int k = this.specStart; + int e = this.specEnd; + int r = 0; + Span componentBlockDataSpan = component.BlockData.Span; + while (k <= e) + { + byte z = PdfJsQuantizationTables.DctZigZag[k]; + switch (this.successiveACState) + { + case 0: // Initial state + short rs = this.DecodeHuffman(ref acHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int s = rs & 15; + r = rs >> 4; + if (s == 0) + { + if (r < 15) + { + this.eobrun = this.Receive(r, stream) + (1 << r); + this.successiveACState = 4; + } + else + { + r = 16; + this.successiveACState = 1; + } + } + else + { + if (s != 1) + { + throw new ImageFormatException("Invalid ACn encoding"); + } + + this.successiveACNextValue = this.ReceiveAndExtend(s, stream); + this.successiveACState = r > 0 ? 2 : 3; + } + + continue; + case 1: // Skipping r zero items + case 2: + if (componentBlockDataSpan[offset + z] != 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + } + else + { + r--; + if (r == 0) + { + this.successiveACState = this.successiveACState == 2 ? 3 : 0; + } + } + + break; + case 3: // Set value for a zero item + if (componentBlockDataSpan[offset + z] != 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + } + else + { + componentBlockDataSpan[offset + z] = (short)(this.successiveACNextValue << this.successiveState); + this.successiveACState = 0; + } + + break; + case 4: // Eob + if (componentBlockDataSpan[offset + z] != 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + } + + break; + } + + k++; + } + + if (this.successiveACState == 4) + { + this.eobrun--; + if (this.eobrun == 0) + { + this.successiveACState = 0; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs similarity index 51% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs rename to src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs index 5c9e8f9fc3..ddc577270b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs @@ -1,71 +1,70 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ /// /// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace. /// Methods to build the tables are based on libjpeg implementation. /// - internal unsafe struct YCbCrToRgbTables + internal struct PdfJsYCbCrToRgbTables { /// /// The red red-chrominance table /// - public fixed int CrRTable[256]; + public static int[] CrRTable = new int[256]; /// /// The blue blue-chrominance table /// - public fixed int CbBTable[256]; + public static int[] CbBTable = new int[256]; /// /// The green red-chrominance table /// - public fixed int CrGTable[256]; + public static int[] CrGTable = new int[256]; /// /// The green blue-chrominance table /// - public fixed int CbGTable[256]; + public static int[] CbGTable = new int[256]; // Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places. private const int ScaleBits = 16; private const int Half = 1 << (ScaleBits - 1); + private const int MinSample = 0; + + private const int HalfSample = 128; + + private const int MaxSample = 255; + /// /// Initializes the YCbCr tables /// - /// The intialized - public static YCbCrToRgbTables Create() + public static void Create() { - YCbCrToRgbTables tables = default(YCbCrToRgbTables); - for (int i = 0, x = -128; i <= 255; i++, x++) { // i is the actual input pixel value, in the range 0..255 // The Cb or Cr value we are thinking of is x = i - 128 // Cr=>R value is nearest int to 1.402 * x - tables.CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); + CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); // Cb=>B value is nearest int to 1.772 * x - tables.CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); + CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); // Cr=>G value is scaled-up -0.714136286 - tables.CrGTable[i] = (-Fix(0.714136286F)) * x; + CrGTable[i] = (-Fix(0.714136286F)) * x; // Cb => G value is scaled - up - 0.344136286 * x // We also add in Half so that need not do it in inner loop - tables.CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; + CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; } - - return tables; } /// @@ -73,27 +72,50 @@ namespace ImageSharp.Formats.Jpg /// /// The pixel format. /// The packed pixel. - /// The reference to the tables instance. /// The y luminance component. /// The cb chroma component. /// The cr chroma component. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Pack(ref TPixel packed, YCbCrToRgbTables* tables, byte y, byte cb, byte cr) + public static void PackYCbCr(ref TPixel packed, byte y, byte cb, byte cr) where TPixel : struct, IPixel { - // float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); - byte r = (byte)(y + tables->CrRTable[cr]).Clamp(0, 255); + byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255); - // float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); // The values for the G calculation are left scaled up, since we must add them together before rounding. - byte g = (byte)(y + RightShift(tables->CbGTable[cb] + tables->CrGTable[cr])).Clamp(0, 255); + byte g = (byte)(y + RightShift(CbGTable[cb] + CrGTable[cr])).Clamp(0, 255); - // float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); - byte b = (byte)(y + tables->CbBTable[cb]).Clamp(0, 255); + byte b = (byte)(y + CbBTable[cb]).Clamp(0, 255); packed.PackFromRgba32(new Rgba32(r, g, b, 255)); } + /// + /// Optimized method to pack bytes to the image from the YccK color space. + /// + /// The pixel format. + /// The packed pixel. + /// The y luminance component. + /// The cb chroma component. + /// The cr chroma component. + /// The keyline component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void PackYccK(ref TPixel packed, byte y, byte cb, byte cr, byte k) + where TPixel : struct, IPixel + { + int c = (MaxSample - (y + CrRTable[cr])).Clamp(0, 255); + + // The values for the G calculation are left scaled up, since we must add them together before rounding. + int m = (MaxSample - (y + RightShift(CbGTable[cb] + CrGTable[cr]))).Clamp(0, 255); + + int cy = (MaxSample - (y + CbBTable[cb])).Clamp(0, 255); + + byte r = (byte)((c * k) / MaxSample); + byte g = (byte)((m * k) / MaxSample); + byte b = (byte)((cy * k) / MaxSample); + + packed.PackFromRgba32(new Rgba32(r, g, b, MaxSample)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Fix(float x) { diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs new file mode 100644 index 0000000000..08b42891d5 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs @@ -0,0 +1,357 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +{ + /// + /// Contains jpeg constant values + /// + internal static class PdfJsJpegConstants + { + /// + /// Contains marker specific constants + /// + public static class Markers + { + /// + /// The prefix used for all markers. + /// + public const byte Prefix = 0xFF; + + /// + /// The Start of Image marker + /// + public const ushort SOI = 0xFFD8; + + /// + /// The End of Image marker + /// + public const ushort EOI = 0xFFD9; + + /// + /// Application specific marker for marking the jpeg format. + /// + /// + public const ushort APP0 = 0xFFE0; + + /// + /// Application specific marker for marking where to store metadata. + /// + public const ushort APP1 = 0xFFE1; + + /// + /// Application specific marker for marking where to store ICC profile information. + /// + public const ushort APP2 = 0xFFE2; + + /// + /// Application specific marker. + /// + public const ushort APP3 = 0xFFE3; + + /// + /// Application specific marker. + /// + public const ushort APP4 = 0xFFE4; + + /// + /// Application specific marker. + /// + public const ushort APP5 = 0xFFE5; + + /// + /// Application specific marker. + /// + public const ushort APP6 = 0xFFE6; + + /// + /// Application specific marker. + /// + public const ushort APP7 = 0xFFE7; + + /// + /// Application specific marker. + /// + public const ushort APP8 = 0xFFE8; + + /// + /// Application specific marker. + /// + public const ushort APP9 = 0xFFE9; + + /// + /// Application specific marker. + /// + public const ushort APP10 = 0xFFEA; + + /// + /// Application specific marker. + /// + public const ushort APP11 = 0xFFEB; + + /// + /// Application specific marker. + /// + public const ushort APP12 = 0xFFEC; + + /// + /// Application specific marker. + /// + public const ushort APP13 = 0xFFED; + + /// + /// Application specific marker used by Adobe for storing encoding information for DCT filters. + /// + public const ushort APP14 = 0xFFEE; + + /// + /// Application specific marker used by GraphicConverter to store JPEG quality. + /// + public const ushort APP15 = 0xFFEF; + + /// + /// The text comment marker + /// + public const ushort COM = 0xFFFE; + + /// + /// Define Quantization Table(s) marker + /// + /// Specifies one or more quantization tables. + /// + /// + public const ushort DQT = 0xFFDB; + + /// + /// Start of Frame (baseline DCT) + /// + /// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components, + /// and component subsampling (e.g., 4:2:0). + /// + /// + public const ushort SOF0 = 0xFFC0; + + /// + /// Start Of Frame (Extended Sequential DCT) + /// + /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, + /// and component subsampling (e.g., 4:2:0). + /// + /// + public const ushort SOF1 = 0xFFC1; + + /// + /// Start Of Frame (progressive DCT) + /// + /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, + /// and component subsampling (e.g., 4:2:0). + /// + /// + public const ushort SOF2 = 0xFFC2; + + /// + /// Define Huffman Table(s) + /// + /// Specifies one or more Huffman tables. + /// + /// + public const ushort DHT = 0xFFC4; + + /// + /// Define Restart Interval + /// + /// Specifies the interval between RSTn markers, in macroblocks.This marker is followed by two bytes indicating the fixed size so it can be treated like any other variable size segment. + /// + /// + public const ushort DRI = 0xFFDD; + + /// + /// Start of Scan + /// + /// Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan. + /// Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it + /// will contain, and is immediately followed by entropy-coded data. + /// + /// + public const ushort SOS = 0xFFDA; + + /// + /// Define First Restart + /// + /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker. + /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7. + /// + /// + public const ushort RST0 = 0xFFD0; + + /// + /// Define Eigth Restart + /// + /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker. + /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7. + /// + /// + public const ushort RST7 = 0xFFD7; + + /// + /// Contains JFIF specific markers + /// + public static class JFif + { + /// + /// Represents J in ASCII + /// + public const byte J = 0x4A; + + /// + /// Represents F in ASCII + /// + public const byte F = 0x46; + + /// + /// Represents I in ASCII + /// + public const byte I = 0x49; + + /// + /// Represents the null "0" marker + /// + public const byte Null = 0x0; + } + + /// + /// Contains Adobe specific markers + /// + public static class Adobe + { + /// + /// Represents A in ASCII + /// + public const byte A = 0x41; + + /// + /// Represents d in ASCII + /// + public const byte D = 0x64; + + /// + /// Represents b in ASCII + /// + public const byte O = 0x6F; + + /// + /// Represents b in ASCII + /// + public const byte B = 0x62; + + /// + /// Represents e in ASCII + /// + public const byte E = 0x65; + + /// + /// The color transform is unknown.(RGB or CMYK) + /// + public const byte ColorTransformUnknown = 0; + + /// + /// The color transform is YCbCr (luminance, red chroma, blue chroma) + /// + public const byte ColorTransformYCbCr = 1; + + /// + /// The color transform is YCCK (luminance, red chroma, blue chroma, keyline) + /// + public const byte ColorTransformYcck = 2; + } + + /// + /// Contains EXIF specific markers + /// + public static class Exif + { + /// + /// Represents E in ASCII + /// + public const byte E = 0x45; + + /// + /// Represents x in ASCII + /// + public const byte X = 0x78; + + /// + /// Represents i in ASCII + /// + public const byte I = 0x69; + + /// + /// Represents f in ASCII + /// + public const byte F = 0x66; + + /// + /// Represents the null "0" marker + /// + public const byte Null = 0x0; + } + + /// + /// Contains ICC specific markers + /// + public static class ICC + { + /// + /// Represents I in ASCII + /// + public const byte I = 0x49; + + /// + /// Represents C in ASCII + /// + public const byte C = 0x43; + + /// + /// Represents _ in ASCII + /// + public const byte UnderScore = 0x5F; + + /// + /// Represents P in ASCII + /// + public const byte P = 0x50; + + /// + /// Represents R in ASCII + /// + public const byte R = 0x52; + + /// + /// Represents O in ASCII + /// + public const byte O = 0x4F; + + /// + /// Represents F in ASCII + /// + public const byte F = 0x46; + + /// + /// Represents L in ASCII + /// + public const byte L = 0x4C; + + /// + /// Represents E in ASCII + /// + public const byte E = 0x45; + + /// + /// Represents the null "0" marker + /// + public const byte Null = 0x0; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs new file mode 100644 index 0000000000..37ce0151f3 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +{ + /// + /// Image decoder for generating an image out of a jpg stream. + /// + internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions + { + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; set; } + + /// + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) + { + return decoder.Decode(stream); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs new file mode 100644 index 0000000000..211c24d208 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -0,0 +1,938 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +{ + /// + /// Performs the jpeg decoding operation. + /// Ported from with additional fixes to handle common encoding errors + /// + internal sealed class PdfJsJpegDecoderCore : IDisposable + { +#pragma warning disable SA1401 // Fields should be private + /// + /// The global configuration + /// + private readonly Configuration configuration; + + /// + /// Gets the temporary buffer used to store bytes read from the stream. + /// + private readonly byte[] temp = new byte[2 * 16 * 4]; + + private readonly byte[] markerBuffer = new byte[2]; + + private PdfJsQuantizationTables quantizationTables; + + private PdfJsHuffmanTables dcHuffmanTables; + + private PdfJsHuffmanTables acHuffmanTables; + + private PdfJsComponentBlocks components; + + private PdfJsJpegPixelArea pixelArea; + + private ushort resetInterval; + + /// + /// Whether the image has a EXIF header + /// + private bool isExif; + + /// + /// Contains information about the JFIF marker + /// + private JFifMarker jFif; + + /// + /// Contains information about the Adobe marker + /// + private AdobeMarker adobe; + + /// + /// Initializes static members of the class. + /// + static PdfJsJpegDecoderCore() + { + PdfJsYCbCrToRgbTables.Create(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public PdfJsJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) + { + this.configuration = configuration ?? Configuration.Default; + this.IgnoreMetadata = options.IgnoreMetadata; + } + + /// + /// Gets the frame + /// + public PdfJsFrame Frame { get; private set; } + + /// + /// Gets the image width + /// + public int ImageWidth { get; private set; } + + /// + /// Gets the image height + /// + public int ImageHeight { get; private set; } + + /// + /// Gets the number of components + /// + public int NumberOfComponents { get; private set; } + + /// + /// Gets the input stream. + /// + public Stream InputStream { get; private set; } + + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; } + + /// + /// Finds the next file marker within the byte stream. + /// + /// The buffer to read file markers to + /// The input stream + /// The + public static PdfJsFileMarker FindNextFileMarker(byte[] marker, Stream stream) + { + int value = stream.Read(marker, 0, 2); + + if (value == 0) + { + return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); + } + + if (marker[0] == PdfJsJpegConstants.Markers.Prefix) + { + // 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." + while (marker[1] == PdfJsJpegConstants.Markers.Prefix) + { + int suffix = stream.ReadByte(); + if (suffix == -1) + { + return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); + } + + marker[1] = (byte)value; + } + + return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2)); + } + + return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2), true); + } + + /// + /// Decodes the image from the specified and sets the data to image. + /// + /// The pixel format. + /// The stream, where the image should be. + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : struct, IPixel + { + ImageMetaData metadata = this.ParseStream(stream); + + this.QuantizeAndInverseAllComponents(); + + var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); + this.FillPixelData(image.Frames.RootFrame); + this.AssignResolution(image); + return image; + } + + /// + public void Dispose() + { + this.Frame?.Dispose(); + this.components?.Dispose(); + this.quantizationTables?.Dispose(); + this.dcHuffmanTables?.Dispose(); + this.acHuffmanTables?.Dispose(); + this.pixelArea.Dispose(); + + // Set large fields to null. + this.Frame = null; + this.components = null; + this.quantizationTables = null; + this.dcHuffmanTables = null; + this.acHuffmanTables = null; + } + + internal ImageMetaData ParseStream(Stream stream) + { + this.InputStream = stream; + + var metadata = new ImageMetaData(); + this.ParseStream(metadata, false); + return metadata; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetBlockBufferOffset(ref PdfJsComponent component, int row, int col) + { + return 64 * (((component.BlocksPerLine + 1) * row) + col); + } + + /// + /// Parses the input stream for file markers + /// + /// Contains the metadata for an image + /// Whether to decode metadata only. + private void ParseStream(ImageMetaData metaData, bool metadataOnly) + { + // TODO: metadata only logic + // Check for the Start Of Image marker. + var fileMarker = new PdfJsFileMarker(this.ReadUint16(), 0); + if (fileMarker.Marker != PdfJsJpegConstants.Markers.SOI) + { + throw new ImageFormatException("Missing SOI marker."); + } + + ushort marker = this.ReadUint16(); + fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2); + + this.quantizationTables = new PdfJsQuantizationTables(); + this.dcHuffmanTables = new PdfJsHuffmanTables(); + this.acHuffmanTables = new PdfJsHuffmanTables(); + + while (fileMarker.Marker != PdfJsJpegConstants.Markers.EOI) + { + // Get the marker length + int remaining = this.ReadUint16() - 2; + + switch (fileMarker.Marker) + { + case PdfJsJpegConstants.Markers.APP0: + this.ProcessApplicationHeaderMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.APP1: + this.ProcessApp1Marker(remaining, metaData); + break; + + case PdfJsJpegConstants.Markers.APP2: + this.ProcessApp2Marker(remaining, metaData); + break; + case PdfJsJpegConstants.Markers.APP3: + case PdfJsJpegConstants.Markers.APP4: + case PdfJsJpegConstants.Markers.APP5: + case PdfJsJpegConstants.Markers.APP6: + case PdfJsJpegConstants.Markers.APP7: + case PdfJsJpegConstants.Markers.APP8: + case PdfJsJpegConstants.Markers.APP9: + case PdfJsJpegConstants.Markers.APP10: + case PdfJsJpegConstants.Markers.APP11: + case PdfJsJpegConstants.Markers.APP12: + case PdfJsJpegConstants.Markers.APP13: + this.InputStream.Skip(remaining); + break; + + case PdfJsJpegConstants.Markers.APP14: + this.ProcessApp14Marker(remaining); + break; + + case PdfJsJpegConstants.Markers.APP15: + case PdfJsJpegConstants.Markers.COM: + this.InputStream.Skip(remaining); + break; + + case PdfJsJpegConstants.Markers.DQT: + this.ProcessDefineQuantizationTablesMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.SOF0: + case PdfJsJpegConstants.Markers.SOF1: + case PdfJsJpegConstants.Markers.SOF2: + this.ProcessStartOfFrameMarker(remaining, fileMarker); + break; + + case PdfJsJpegConstants.Markers.DHT: + this.ProcessDefineHuffmanTablesMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.DRI: + this.ProcessDefineRestartIntervalMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.SOS: + this.ProcessStartOfScanMarker(); + break; + } + + // Read on. + fileMarker = FindNextFileMarker(this.markerBuffer, this.InputStream); + } + + this.ImageWidth = this.Frame.SamplesPerLine; + this.ImageHeight = this.Frame.Scanlines; + this.components = new PdfJsComponentBlocks { Components = new PdfJsComponent[this.Frame.ComponentCount] }; + + for (int i = 0; i < this.components.Components.Length; i++) + { + PdfJsFrameComponent frameComponent = this.Frame.Components[i]; + var component = new PdfJsComponent + { + Scale = new System.Numerics.Vector2( + frameComponent.HorizontalSamplingFactor / (float)this.Frame.MaxHorizontalFactor, + frameComponent.VerticalSamplingFactor / (float)this.Frame.MaxVerticalFactor), + BlocksPerLine = frameComponent.WidthInBlocks, + BlocksPerColumn = frameComponent.HeightInBlocks + }; + + // this.QuantizeAndInverseComponentData(ref component, frameComponent); + this.components.Components[i] = component; + } + + this.NumberOfComponents = this.components.Components.Length; + } + + internal void QuantizeAndInverseAllComponents() + { + for (int i = 0; i < this.components.Components.Length; i++) + { + PdfJsFrameComponent frameComponent = this.Frame.Components[i]; + PdfJsComponent component = this.components.Components[i]; + + this.QuantizeAndInverseComponentData(component, frameComponent); + } + } + + /// + /// Fills the given image with the color data + /// + /// The pixel format. + /// The image + private void FillPixelData(ImageFrame image) + where TPixel : struct, IPixel + { + if (this.NumberOfComponents > 4) + { + throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.NumberOfComponents}"); + } + + this.pixelArea = new PdfJsJpegPixelArea(image.Width, image.Height, this.NumberOfComponents); + this.pixelArea.LinearizeBlockData(this.components, image.Width, image.Height); + + if (this.NumberOfComponents == 1) + { + this.FillGrayScaleImage(image); + return; + } + + if (this.NumberOfComponents == 3) + { + if (this.adobe.Equals(default(AdobeMarker)) || this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYCbCr) + { + this.FillYCbCrImage(image); + } + else if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformUnknown) + { + this.FillRgbImage(image); + } + } + + if (this.NumberOfComponents == 4) + { + if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck) + { + this.FillYcckImage(image); + } + else + { + this.FillCmykImage(image); + } + } + } + + /// + /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata. + /// + /// The pixel format. + /// The image to assign the resolution to. + private void AssignResolution(Image image) + where TPixel : struct, IPixel + { + if (this.isExif) + { + ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); + double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; + double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; + + if (horizontalValue > 0 && verticalValue > 0) + { + image.MetaData.HorizontalResolution = horizontalValue; + image.MetaData.VerticalResolution = verticalValue; + } + } + else if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0) + { + image.MetaData.HorizontalResolution = this.jFif.XDensity; + image.MetaData.VerticalResolution = this.jFif.YDensity; + } + } + + /// + /// Processes the application header containing the JFIF identifier plus extra data. + /// + /// The remaining bytes in the segment block. + private void ProcessApplicationHeaderMarker(int remaining) + { + if (remaining < 5) + { + // Skip the application header length + this.InputStream.Skip(remaining); + return; + } + + this.InputStream.Read(this.temp, 0, JFifMarker.Length); + remaining -= JFifMarker.Length; + + JFifMarker.TryParse(this.temp, out this.jFif); + + // TODO: thumbnail + if (remaining > 0) + { + this.InputStream.Skip(remaining); + } + } + + /// + /// Processes the App1 marker retrieving any stored metadata + /// + /// The remaining bytes in the segment block. + /// The image. + private void ProcessApp1Marker(int remaining, ImageMetaData metadata) + { + if (remaining < 6 || this.IgnoreMetadata) + { + // Skip the application header length + this.InputStream.Skip(remaining); + return; + } + + byte[] profile = new byte[remaining]; + this.InputStream.Read(profile, 0, remaining); + + if (profile[0] == PdfJsJpegConstants.Markers.Exif.E && + profile[1] == PdfJsJpegConstants.Markers.Exif.X && + profile[2] == PdfJsJpegConstants.Markers.Exif.I && + profile[3] == PdfJsJpegConstants.Markers.Exif.F && + profile[4] == PdfJsJpegConstants.Markers.Exif.Null && + profile[5] == PdfJsJpegConstants.Markers.Exif.Null) + { + this.isExif = true; + metadata.ExifProfile = new ExifProfile(profile); + } + } + + /// + /// Processes the App2 marker retrieving any stored ICC profile information + /// + /// The remaining bytes in the segment block. + /// The image. + private void ProcessApp2Marker(int remaining, ImageMetaData metadata) + { + // Length is 14 though we only need to check 12. + const int Icclength = 14; + if (remaining < Icclength || this.IgnoreMetadata) + { + this.InputStream.Skip(remaining); + return; + } + + byte[] identifier = new byte[Icclength]; + this.InputStream.Read(identifier, 0, Icclength); + remaining -= Icclength; // We have read it by this point + + if (identifier[0] == PdfJsJpegConstants.Markers.ICC.I && + identifier[1] == PdfJsJpegConstants.Markers.ICC.C && + identifier[2] == PdfJsJpegConstants.Markers.ICC.C && + identifier[3] == PdfJsJpegConstants.Markers.ICC.UnderScore && + identifier[4] == PdfJsJpegConstants.Markers.ICC.P && + identifier[5] == PdfJsJpegConstants.Markers.ICC.R && + identifier[6] == PdfJsJpegConstants.Markers.ICC.O && + identifier[7] == PdfJsJpegConstants.Markers.ICC.F && + identifier[8] == PdfJsJpegConstants.Markers.ICC.I && + identifier[9] == PdfJsJpegConstants.Markers.ICC.L && + identifier[10] == PdfJsJpegConstants.Markers.ICC.E && + identifier[11] == PdfJsJpegConstants.Markers.ICC.Null) + { + byte[] profile = new byte[remaining]; + this.InputStream.Read(profile, 0, remaining); + + if (metadata.IccProfile == null) + { + metadata.IccProfile = new IccProfile(profile); + } + else + { + metadata.IccProfile.Extend(profile); + } + } + else + { + // Not an ICC profile we can handle. Skip the remaining bytes so we can carry on and ignore this. + this.InputStream.Skip(remaining); + } + } + + /// + /// Processes the application header containing the Adobe identifier + /// which stores image encoding information for DCT filters. + /// + /// The remaining bytes in the segment block. + private void ProcessApp14Marker(int remaining) + { + const int MarkerLength = AdobeMarker.Length; + if (remaining < MarkerLength) + { + // Skip the application header length + this.InputStream.Skip(remaining); + return; + } + + this.InputStream.Read(this.temp, 0, MarkerLength); + remaining -= MarkerLength; + + AdobeMarker.TryParse(this.temp, out this.adobe); + + if (remaining > 0) + { + this.InputStream.Skip(remaining); + } + } + + /// + /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. + /// + /// The remaining bytes in the segment block. + /// + /// Thrown if the tables do not match the header + /// + private void ProcessDefineQuantizationTablesMarker(int remaining) + { + while (remaining > 0) + { + bool done = false; + remaining--; + int quantizationTableSpec = this.InputStream.ReadByte(); + + switch (quantizationTableSpec >> 4) + { + case 0: + { + // 8 bit values + if (remaining < 64) + { + done = true; + break; + } + + this.InputStream.Read(this.temp, 0, 64); + remaining -= 64; + + Span tableSpan = this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15); + for (int j = 0; j < 64; j++) + { + tableSpan[PdfJsQuantizationTables.DctZigZag[j]] = this.temp[j]; + } + } + + break; + case 1: + { + // 16 bit values + if (remaining < 128) + { + done = true; + break; + } + + this.InputStream.Read(this.temp, 0, 128); + remaining -= 128; + + Span tableSpan = this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15); + for (int j = 0; j < 64; j++) + { + tableSpan[PdfJsQuantizationTables.DctZigZag[j]] = (short)((this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]); + } + } + + break; + default: + throw new ImageFormatException("Bad Tq index value"); + } + + if (done) + { + break; + } + } + + if (remaining != 0) + { + throw new ImageFormatException("DQT has wrong length"); + } + } + + /// + /// Processes the Start of Frame marker. Specified in section B.2.2. + /// + /// The remaining bytes in the segment block. + /// The current frame marker. + private void ProcessStartOfFrameMarker(int remaining, PdfJsFileMarker frameMarker) + { + if (this.Frame != null) + { + throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported."); + } + + this.InputStream.Read(this.temp, 0, remaining); + + this.Frame = new PdfJsFrame + { + Extended = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF1, + Progressive = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF2, + Precision = this.temp[0], + Scanlines = (short)((this.temp[1] << 8) | this.temp[2]), + SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]), + ComponentCount = this.temp[5] + }; + + int maxH = 0; + int maxV = 0; + int index = 6; + + // No need to pool this. They max out at 4 + this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; + this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount]; + + for (int i = 0; i < this.Frame.Components.Length; i++) + { + int h = this.temp[index + 1] >> 4; + int v = this.temp[index + 1] & 15; + + if (maxH < h) + { + maxH = h; + } + + if (maxV < v) + { + maxV = v; + } + + var component = new PdfJsFrameComponent(this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + + this.Frame.Components[i] = component; + this.Frame.ComponentIds[i] = component.Id; + + index += 3; + } + + this.Frame.MaxHorizontalFactor = maxH; + this.Frame.MaxVerticalFactor = maxV; + this.Frame.InitComponents(); + } + + /// + /// Processes a Define Huffman Table marker, and initializes a huffman + /// struct from its contents. Specified in section B.2.4.2. + /// + /// The remaining bytes in the segment block. + private void ProcessDefineHuffmanTablesMarker(int remaining) + { + if (remaining < 17) + { + throw new ImageFormatException($"DHT has wrong length: {remaining}"); + } + + using (var huffmanData = Buffer.CreateClean(256)) + { + for (int i = 2; i < remaining;) + { + byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); + this.InputStream.Read(huffmanData.Array, 0, 16); + + using (var codeLengths = Buffer.CreateClean(17)) + { + int codeLengthSum = 0; + + for (int j = 1; j < 17; j++) + { + codeLengthSum += codeLengths[j] = huffmanData[j - 1]; + } + + using (var huffmanValues = Buffer.CreateClean(256)) + { + this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); + + i += 17 + codeLengthSum; + + this.BuildHuffmanTable( + huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables, + huffmanTableSpec & 15, + codeLengths.Array, + huffmanValues.Array); + } + } + } + } + } + + /// + /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in + /// macroblocks + /// + /// The remaining bytes in the segment block. + private void ProcessDefineRestartIntervalMarker(int remaining) + { + if (remaining != 2) + { + throw new ImageFormatException($"DRI has wrong length: {remaining}"); + } + + this.resetInterval = this.ReadUint16(); + } + + /// + /// Processes the SOS (Start of scan marker). + /// + private void ProcessStartOfScanMarker() + { + int selectorsCount = this.InputStream.ReadByte(); + int componentIndex = -1; + for (int i = 0; i < selectorsCount; i++) + { + componentIndex = -1; + int selector = this.InputStream.ReadByte(); + + for (int j = 0; j < this.Frame.ComponentIds.Length; j++) + { + byte id = this.Frame.ComponentIds[j]; + if (selector == id) + { + componentIndex = j; + } + } + + if (componentIndex < 0) + { + throw new ImageFormatException("Unknown component selector"); + } + + ref PdfJsFrameComponent component = ref this.Frame.Components[componentIndex]; + int tableSpec = this.InputStream.ReadByte(); + component.DCHuffmanTableId = tableSpec >> 4; + component.ACHuffmanTableId = tableSpec & 15; + } + + this.InputStream.Read(this.temp, 0, 3); + + int spectralStart = this.temp[0]; + int spectralEnd = this.temp[1]; + int successiveApproximation = this.temp[2]; + var scanDecoder = default(PdfJsScanDecoder); + + scanDecoder.DecodeScan( + this.Frame, + this.InputStream, + this.dcHuffmanTables, + this.acHuffmanTables, + this.Frame.Components, + componentIndex, + selectorsCount, + this.resetInterval, + spectralStart, + spectralEnd, + successiveApproximation >> 4, + successiveApproximation & 15); + } + + /// + /// Build the data for the given component + /// + /// The component + /// The frame component + private void QuantizeAndInverseComponentData(PdfJsComponent component, PdfJsFrameComponent frameComponent) + { + int blocksPerLine = component.BlocksPerLine; + int blocksPerColumn = component.BlocksPerColumn; + using (var computationBuffer = Buffer.CreateClean(64)) + using (var multiplicationBuffer = Buffer.CreateClean(64)) + { + Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); + Span computationBufferSpan = computationBuffer; + + // For AA&N IDCT method, multiplier are equal to quantization + // coefficients scaled by scalefactor[row]*scalefactor[col], where + // scalefactor[0] = 1 + // scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + // For integer operation, the multiplier table is to be scaled by 12. + Span multiplierSpan = multiplicationBuffer; + + // for (int i = 0; i < 64; i++) + // { + // multiplierSpan[i] = (short)IDCT.Descale(quantizationTable[i] * IDCT.Aanscales[i], 12); + // } + for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) + { + for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) + { + int offset = GetBlockBufferOffset(ref component, blockRow, blockCol); + PdfJsIDCT.QuantizeAndInverse(frameComponent, offset, ref computationBufferSpan, ref quantizationTable); + } + } + } + + component.Output = frameComponent.BlockData; + } + + /// + /// Builds the huffman tables + /// + /// The tables + /// The table index + /// The codelengths + /// The values + private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, byte[] codeLengths, byte[] values) + { + tables[index] = new PdfJsHuffmanTable(codeLengths, values); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillGrayScaleImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + + for (int x = 0; x < image.Width; x++) + { + ref byte luminance = ref areaRowSpan[x]; + ref TPixel pixel = ref imageRowSpan[x]; + var rgba = new Rgba32(luminance, luminance, luminance); + pixel.PackFromRgba32(rgba); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillYCbCrImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + for (int x = 0, o = 0; x < image.Width; x++, o += 3) + { + ref byte yy = ref areaRowSpan[o]; + ref byte cb = ref areaRowSpan[o + 1]; + ref byte cr = ref areaRowSpan[o + 2]; + ref TPixel pixel = ref imageRowSpan[x]; + PdfJsYCbCrToRgbTables.PackYCbCr(ref pixel, yy, cb, cr); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillYcckImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + for (int x = 0, o = 0; x < image.Width; x++, o += 4) + { + ref byte yy = ref areaRowSpan[o]; + ref byte cb = ref areaRowSpan[o + 1]; + ref byte cr = ref areaRowSpan[o + 2]; + ref byte k = ref areaRowSpan[o + 3]; + + ref TPixel pixel = ref imageRowSpan[x]; + PdfJsYCbCrToRgbTables.PackYccK(ref pixel, yy, cb, cr, k); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillCmykImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + for (int x = 0, o = 0; x < image.Width; x++, o += 4) + { + ref byte c = ref areaRowSpan[o]; + ref byte m = ref areaRowSpan[o + 1]; + ref byte cy = ref areaRowSpan[o + 2]; + ref byte k = ref areaRowSpan[o + 3]; + + byte r = (byte)((c * k) / 255); + byte g = (byte)((m * k) / 255); + byte b = (byte)((cy * k) / 255); + + ref TPixel pixel = ref imageRowSpan[x]; + var rgba = new Rgba32(r, g, b); + pixel.PackFromRgba32(rgba); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillRgbImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + + PixelOperations.Instance.PackFromRgb24Bytes(areaRowSpan, imageRowSpan, image.Width); + } + } + + /// + /// Reads a from the stream advancing it by two bytes + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ushort ReadUint16() + { + this.InputStream.Read(this.markerBuffer, 0, 2); + return (ushort)((this.markerBuffer[0] << 8) | this.markerBuffer[1]); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpan.cs b/src/ImageSharp/Formats/Jpeg/Utils/MutableSpan.cs deleted file mode 100644 index 99d1c3e04e..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpan.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - - /// - /// Like corefxlab Span, but with an AddOffset() method for efficiency. - /// TODO: When Span will be official, consider replacing this class! - /// - /// - /// https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs - /// - /// The type of the data in the span - internal struct MutableSpan - { - /// - /// Data - /// - public T[] Data; - - /// - /// Offset - /// - public int Offset; - - /// - /// Initializes a new instance of the struct. - /// - /// The size of the span - /// The offset (defaults to 0) - public MutableSpan(int size, int offset = 0) - { - this.Data = new T[size]; - this.Offset = offset; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The data - /// The offset (defaults to 0) - public MutableSpan(T[] data, int offset = 0) - { - this.Data = data; - this.Offset = offset; - } - - /// - /// Gets the total count of data - /// - public int TotalCount => this.Data.Length - this.Offset; - - /// - /// Index into the data - /// - /// The data - /// The value at the specified index - public T this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.Data[idx + this.Offset]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.Data[idx + this.Offset] = value; - } - } - - public static implicit operator MutableSpan(T[] data) => new MutableSpan(data, 0); - - /// - /// Slice the data - /// - /// The offset - /// The new - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MutableSpan Slice(int offset) - { - return new MutableSpan(this.Data, this.Offset + offset); - } - - /// - /// Add to the offset - /// - /// The additional offset - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddOffset(int offset) - { - this.Offset += offset; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpanExtensions.cs b/src/ImageSharp/Formats/Jpeg/Utils/MutableSpanExtensions.cs deleted file mode 100644 index 45ecfc0920..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpanExtensions.cs +++ /dev/null @@ -1,164 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - /// - /// MutableSpan Extensions - /// - internal static class MutableSpanExtensions - { - /// - /// Slice - /// - /// The type of the data in the span - /// The data array - /// The offset - /// The new - public static MutableSpan Slice(this T[] array, int offset) => new MutableSpan(array, offset); - - /// - /// Save to a Vector4 - /// - /// The data - /// The vector to save to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SaveTo(this MutableSpan data, ref Vector4 v) - { - v.X = data[0]; - v.Y = data[1]; - v.Z = data[2]; - v.W = data[3]; - } - - /// - /// Save to a Vector4 - /// - /// The data - /// The vector to save to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SaveTo(this MutableSpan data, ref Vector4 v) - { - v.X = data[0]; - v.Y = data[1]; - v.Z = data[2]; - v.W = data[3]; - } - - /// - /// Load from Vector4 - /// - /// The data - /// The vector to load from - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void LoadFrom(this MutableSpan data, ref Vector4 v) - { - data[0] = v.X; - data[1] = v.Y; - data[2] = v.Z; - data[3] = v.W; - } - - /// - /// Load from Vector4 - /// - /// The data - /// The vector to load from - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void LoadFrom(this MutableSpan data, ref Vector4 v) - { - data[0] = (int)v.X; - data[1] = (int)v.Y; - data[2] = (int)v.Z; - data[3] = (int)v.W; - } - - /// - /// Converts all int values of src to float - /// - /// Source - /// A new with float values - public static MutableSpan ConvertToFloat32MutableSpan(this MutableSpan src) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = (float)src[i]; - } - - return result; - } - - /// - /// Converts all float values of src to int - /// - /// Source - /// A new with float values - public static MutableSpan ConvertToInt32MutableSpan(this MutableSpan src) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = (int)src[i]; - } - - return result; - } - - /// - /// Add a scalar to all values of src - /// - /// The source - /// The scalar value to add - /// A new instance of - public static MutableSpan AddScalarToAllValues(this MutableSpan src, float scalar) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = src[i] + scalar; - } - - return result; - } - - /// - /// Add a scalar to all values of src - /// - /// The source - /// The scalar value to add - /// A new instance of - public static MutableSpan AddScalarToAllValues(this MutableSpan src, int scalar) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = src[i] + scalar; - } - - return result; - } - - /// - /// Copy all values in src to a new instance - /// - /// Element type - /// The source - /// A new instance of - public static MutableSpan Copy(this MutableSpan src) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = src[i]; - } - - return result; - } - } -} diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs new file mode 100644 index 0000000000..ed21b91bfc --- /dev/null +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Contains information about the pixels that make up an images visual data. + /// + public class PixelTypeInfo + { + /// + /// Initializes a new instance of the class. + /// + /// Color depth, in number of bits per pixel. + internal PixelTypeInfo(int bitsPerPixel) + { + this.BitsPerPixel = bitsPerPixel; + } + + /// + /// Gets color depth, in number of bits per pixel. + /// + public int BitsPerPixel { get; } + } +} diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index db2189b3c4..0d3a65dbd8 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Average filter uses the average of the two neighboring pixels (left and above) to predict /// the value of a pixel. @@ -55,8 +53,9 @@ namespace ImageSharp.Formats /// The previous scanline. /// The filtered scanline result. /// The bytes per pixel. + /// The sum of the total variance of the filtered row [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel) + public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum) { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); @@ -64,6 +63,7 @@ namespace ImageSharp.Formats ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + sum = 0; // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) resultBaseRef = 3; @@ -76,6 +76,7 @@ namespace ImageSharp.Formats byte above = Unsafe.Add(ref prevBaseRef, x); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - (above >> 1)) % 256); + sum += res < 128 ? res : 256 - res; } else { @@ -84,8 +85,11 @@ namespace ImageSharp.Formats byte above = Unsafe.Add(ref prevBaseRef, x); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - Average(left, above)) % 256); + sum += res < 128 ? res : 256 - res; } } + + sum -= 3; } /// diff --git a/src/ImageSharp/Formats/Png/Filters/FilterType.cs b/src/ImageSharp/Formats/Png/Filters/FilterType.cs index 0eddf08f20..83a005380a 100644 --- a/src/ImageSharp/Formats/Png/Filters/FilterType.cs +++ b/src/ImageSharp/Formats/Png/Filters/FilterType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png.Filters { /// /// Provides enumeration of the various PNG filter types. diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs index ee77d1a5eb..0164ceafaa 100644 --- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The None filter, the scanline is transmitted unmodified; it is only necessary to /// insert a filter type byte before the data. diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index ec12eca058..08e4938804 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left), /// then chooses as predictor the neighboring pixel closest to the computed value. /// This technique is due to Alan W. Paeth. /// /// - internal static unsafe class PaethFilter + internal static class PaethFilter { /// /// Decodes the scanline @@ -31,22 +29,21 @@ namespace ImageSharp.Formats ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - for (int x = 1; x < scanline.Length; x++) + int offset = bytesPerPixel + 1; + for (int x = 1; x < offset; x++) { - if (x - bytesPerPixel < 1) - { - ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); - byte above = Unsafe.Add(ref prevBaseRef, x); - scan = (byte)((scan + PaethPredicator(0, above, 0)) % 256); - } - else - { - ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); - byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); - byte above = Unsafe.Add(ref prevBaseRef, x); - byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); - scan = (byte)((scan + PaethPredicator(left, above, upperLeft)) % 256); - } + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + scan = (byte)(scan + above); + } + + for (int x = offset; x < scanline.Length; x++) + { + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); + scan = (byte)(scan + PaethPredicator(left, above, upperLeft)); } } @@ -57,8 +54,9 @@ namespace ImageSharp.Formats /// The previous scanline. /// The filtered scanline result. /// The bytes per pixel. + /// The sum of the total variance of the filtered row [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel) + public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel, out int sum) { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); @@ -66,6 +64,7 @@ namespace ImageSharp.Formats ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + sum = 0; // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) resultBaseRef = 4; @@ -78,6 +77,7 @@ namespace ImageSharp.Formats byte above = Unsafe.Add(ref prevBaseRef, x); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - PaethPredicator(0, above, 0)) % 256); + sum += res < 128 ? res : 256 - res; } else { @@ -87,8 +87,11 @@ namespace ImageSharp.Formats byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - PaethPredicator(left, above, upperLeft)) % 256); + sum += res < 128 ? res : 256 - res; } } + + sum -= 4; } /// diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index f81122ad24..5ee8664400 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Sub filter transmits the difference between each byte and the value of the corresponding byte /// of the prior pixel. /// /// - internal static unsafe class SubFilter + internal static class SubFilter { /// /// Decodes the scanline @@ -48,13 +46,15 @@ namespace ImageSharp.Formats /// The scanline to encode /// The filtered scanline result. /// The bytes per pixel. + /// The sum of the total variance of the filtered row [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(Span scanline, Span result, int bytesPerPixel) + public static void Encode(Span scanline, Span result, int bytesPerPixel, out int sum) { DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + sum = 0; // Sub(x) = Raw(x) - Raw(x-bpp) resultBaseRef = 1; @@ -66,6 +66,7 @@ namespace ImageSharp.Formats byte scan = Unsafe.Add(ref scanBaseRef, x); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)(scan % 256); + sum += res < 128 ? res : 256 - res; } else { @@ -73,8 +74,11 @@ namespace ImageSharp.Formats byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - prev) % 256); + sum += res < 128 ? res : 256 - res; } } + + sum -= 1; } } } diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index b89a3c5fcc..6e8f780e5c 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Up filter is just like the Sub filter except that the pixel immediately above the current pixel, /// rather than just to its left, is used as the predictor. /// /// - internal static unsafe class UpFilter + internal static class UpFilter { /// /// Decodes the scanline @@ -43,8 +41,9 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. + /// The sum of the total variance of the filtered row [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(Span scanline, Span previousScanline, Span result) + public static void Encode(Span scanline, Span previousScanline, Span result, out int sum) { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); @@ -52,6 +51,7 @@ namespace ImageSharp.Formats ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + sum = 0; // Up(x) = Raw(x) - Prior(x) resultBaseRef = 2; @@ -62,7 +62,10 @@ namespace ImageSharp.Formats byte above = Unsafe.Add(ref prevBaseRef, x); ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); res = (byte)((scan - above) % 256); + sum += res < 128 ? res : 256 - res; } + + sum -= 2; } } } diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs index de163ba7e5..e51cc084b2 100644 --- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// The optioas for decoding png images /// diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 8f0a4cd829..6b4b05e311 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// The options availible for manipulating the encoder pipeline /// diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index c817385760..10970fc16a 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,14 +21,9 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsPng(this Image source, Stream stream) + public static void SaveAsPng(this Image source, Stream stream) where TPixel : struct, IPixel - { - return SaveAsPng(source, stream, null); - } + => SaveAsPng(source, stream, null); /// /// Saves the image to the given stream with the png format. @@ -40,16 +33,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsPng(this Image source, Stream stream, PngEncoder encoder) + public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) where TPixel : struct, IPixel - { - encoder = encoder ?? new PngEncoder(); - encoder.Encode(source, stream); - - return source; - } + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Png)); } } diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 3d769057ca..f90def5b38 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Stores header information about a chunk. diff --git a/src/ImageSharp/Formats/Png/PngChunkTypes.cs b/src/ImageSharp/Formats/Png/PngChunkTypes.cs index 72486b50d7..e22f4f0e7d 100644 --- a/src/ImageSharp/Formats/Png/PngChunkTypes.cs +++ b/src/ImageSharp/Formats/Png/PngChunkTypes.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Contains a list of possible chunk type identifiers. diff --git a/src/ImageSharp/Formats/Png/PngColorType.cs b/src/ImageSharp/Formats/Png/PngColorType.cs index 421e27ba33..fc376ca161 100644 --- a/src/ImageSharp/Formats/Png/PngColorType.cs +++ b/src/ImageSharp/Formats/Png/PngColorType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Provides enumeration of available PNG color types. diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index bb1c2086cc..9346f7567e 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Registers the image encoders, decoders and mime type detectors for the png format. diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs index 84e76a3419..8b4ad39f28 100644 --- a/src/ImageSharp/Formats/Png/PngConstants.cs +++ b/src/ImageSharp/Formats/Png/PngConstants.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.Text; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Defines png constants defined in the specification. /// diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 61a8cb2127..9bde4f8cc3 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Encoder for generating an image out of a png encoded stream. /// @@ -31,7 +27,7 @@ namespace ImageSharp.Formats /// /// /// - public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions + public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector { /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. @@ -44,7 +40,7 @@ namespace ImageSharp.Formats public Encoding TextEncoding { get; set; } = PngConstants.DefaultEncoding; /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// /// The pixel format. /// The configuration for the image. @@ -56,5 +52,12 @@ namespace ImageSharp.Formats var decoder = new PngDecoderCore(configuration, this); return decoder.Decode(stream); } + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + var decoder = new PngDecoderCore(configuration, this); + return decoder.Identify(stream); + } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index e6f19b9157..e39187e086 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1,22 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png.Filters; +using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Png { - using System; - using System.Buffers; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - using System.Text; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - using static ComparableExtensions; - /// /// Performs the png decoding operation. /// @@ -84,6 +84,16 @@ namespace ImageSharp.Formats /// private readonly Configuration configuration; + /// + /// Gets the encoding to use + /// + private readonly Encoding textEncoding; + + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + private readonly bool ignoreMetadata; + /// /// The stream to decode from. /// @@ -155,14 +165,19 @@ namespace ImageSharp.Formats private PngColorType pngColorType; /// - /// Gets the encoding to use + /// Represents any color in an Rgb24 encoded png that should be transparent /// - private Encoding textEncoding; + private Rgb24 rgb24Trans; /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// Represents any color in a Grayscale encoded png that should be transparent /// - private bool ignoreMetadata; + private byte intensityTrans; + + /// + /// Whether the image has transparency chunk and markers were decoded + /// + private bool hasTrans; /// /// Initializes a new instance of the class. @@ -220,8 +235,8 @@ namespace ImageSharp.Formats } deframeStream.AllocateNewBytes(currentChunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image); - stream.Read(this.crcBuffer, 0, 4); + this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); + this.currentStream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: byte[] pal = new byte[currentChunk.Length]; @@ -232,6 +247,7 @@ namespace ImageSharp.Formats byte[] alpha = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); this.paletteAlpha = alpha; + this.AssignTransparentMarkers(alpha); break; case PngChunkTypes.Text: this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); @@ -261,51 +277,130 @@ namespace ImageSharp.Formats } } + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public IImageInfo Identify(Stream stream) + { + var metadata = new ImageMetaData(); + this.currentStream = stream; + this.currentStream.Skip(8); + try + { + PngChunk currentChunk; + while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + { + try + { + switch (currentChunk.Type) + { + case PngChunkTypes.Header: + this.ReadHeaderChunk(currentChunk.Data); + this.ValidateHeader(); + break; + case PngChunkTypes.Physical: + this.ReadPhysicalChunk(metadata, currentChunk.Data); + break; + case PngChunkTypes.Data: + this.SkipChunkDataAndCrc(currentChunk); + break; + case PngChunkTypes.Text: + this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); + break; + case PngChunkTypes.End: + this.isEndChunkReached = true; + break; + } + } + finally + { + // Data is rented in ReadChunkData() + if (currentChunk.Data != null) + { + ArrayPool.Shared.Return(currentChunk.Data); + } + } + } + } + finally + { + this.scanline?.Dispose(); + this.previousScanline?.Dispose(); + } + + if (this.header == null) + { + throw new ImageFormatException("PNG Image does not contain a header chunk"); + } + + return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); + } + /// /// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits. /// /// The bytes to convert from. Cannot be null. /// The number of bytes per scanline /// The number of bits per value. - /// The resulting array. Is never null. + /// The resulting array. Is never null. /// is null. /// is less than or equals than zero. - private static byte[] ToArrayByBitsLength(byte[] source, int bytesPerScanline, int bits) + private static Span ToArrayByBitsLength(Span source, int bytesPerScanline, int bits) { - Guard.NotNull(source, nameof(source)); + Guard.MustBeGreaterThan(source.Length, 0, nameof(source)); Guard.MustBeGreaterThan(bits, 0, nameof(bits)); - byte[] result; - - if (bits < 8) + if (bits >= 8) { - result = new byte[bytesPerScanline * 8 / bits]; - int mask = 0xFF >> (8 - bits); - int resultOffset = 0; + return source; + } + + byte[] result = new byte[bytesPerScanline * 8 / bits]; + int mask = 0xFF >> (8 - bits); + int resultOffset = 0; - // ReSharper disable once ForCanBeConvertedToForeach - // First byte is the marker so skip. - for (int i = 1; i < bytesPerScanline; i++) + for (int i = 0; i < bytesPerScanline - 1; i++) + { + byte b = source[i]; + for (int shift = 0; shift < 8; shift += bits) { - byte b = source[i]; - for (int shift = 0; shift < 8; shift += bits) - { - int colorIndex = (b >> (8 - bits - shift)) & mask; + int colorIndex = (b >> (8 - bits - shift)) & mask; - result[resultOffset] = (byte)colorIndex; + result[resultOffset] = (byte)colorIndex; - resultOffset++; - } + resultOffset++; } } - else - { - result = source; - } return result; } + /// + /// Returns a value indicating whether the given chunk is critical to decoding + /// + /// The chunk + /// The + private static bool IsCriticalChunk(PngChunk chunk) + { + return + chunk.Type == PngChunkTypes.Header || + chunk.Type == PngChunkTypes.Palette || + chunk.Type == PngChunkTypes.Data || + chunk.Type == PngChunkTypes.End; + } + + /// + /// Reads an integer value from 2 consecutive bytes in LSB order + /// + /// The source buffer + /// THe offset + /// The + public static int ReadIntFrom2Bytes(byte[] buffer, int offset) + { + return ((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF); + } + /// /// Reads the data chunk containing physical dimension data. /// @@ -343,6 +438,28 @@ namespace ImageSharp.Formats this.scanline = Buffer.CreateClean(this.bytesPerScanline); } + /// + /// Calculates the correct number of bits per pixel for the given color type. + /// + /// The + private int CalculateBitsPerPixel() + { + switch (this.pngColorType) + { + case PngColorType.Grayscale: + case PngColorType.Palette: + return this.header.BitDepth; + case PngColorType.GrayscaleWithAlpha: + return this.header.BitDepth * 2; + case PngColorType.Rgb: + return this.header.BitDepth * 3; + case PngColorType.RgbWithAlpha: + return this.header.BitDepth * 4; + default: + throw new NotSupportedException("Unsupported PNG color type"); + } + } + /// /// Calculates the correct number of bytes per pixel for the given color type. /// @@ -406,7 +523,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing data. /// The pixel data. - private void ReadScanlines(Stream dataStream, Image image) + private void ReadScanlines(Stream dataStream, ImageFrame image) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) @@ -425,7 +542,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The compressed pixel data stream. /// The image to decode to. - private void DecodePixelData(Stream compressedStream, Image image) + private void DecodePixelData(Stream compressedStream, ImageFrame image) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -471,7 +588,7 @@ namespace ImageSharp.Formats this.ProcessDefilteredScanline(this.scanline.Array, image); - Swap(ref this.scanline, ref this.previousScanline); + this.SwapBuffers(); this.currentRow++; } } @@ -483,7 +600,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The compressed pixel data stream. /// The current image. - private void DecodeInterlacedPixelData(Stream compressedStream, Image image) + private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image) where TPixel : struct, IPixel { while (true) @@ -512,7 +629,7 @@ namespace ImageSharp.Formats this.currentRowBytesRead = 0; Span scanSpan = this.scanline.Slice(0, bytesPerInterlaceScanline); - Span prevSpan = this.previousScanline.Span.Slice(0, bytesPerInterlaceScanline); + Span prevSpan = this.previousScanline.Slice(0, bytesPerInterlaceScanline); var filterType = (FilterType)scanSpan[0]; switch (filterType) @@ -544,21 +661,24 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - Span rowSpan = image.GetRowSpan(this.currentRow); + Span rowSpan = image.GetPixelRowSpan(this.currentRow); this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); - Swap(ref this.scanline, ref this.previousScanline); + this.SwapBuffers(); this.currentRow += Adam7RowIncrement[this.pass]; } this.pass++; + this.previousScanline.Clear(); + if (this.pass < 7) { this.currentRow = Adam7FirstRow[this.pass]; } else { + this.pass = 0; break; } } @@ -570,22 +690,33 @@ namespace ImageSharp.Formats /// The pixel format. /// The de-filtered scanline /// The image - private void ProcessDefilteredScanline(byte[] defilteredScanline, Image pixels) + private void ProcessDefilteredScanline(byte[] defilteredScanline, ImageFrame pixels) where TPixel : struct, IPixel { var color = default(TPixel); - Span rowSpan = pixels.GetRowSpan(this.currentRow); + Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); + + // Trim the first marker byte from the buffer var scanlineBuffer = new Span(defilteredScanline, 1); switch (this.pngColorType) { case PngColorType.Grayscale: int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); - byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + Span newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); + for (int x = 0; x < this.header.Width; x++) { byte intensity = (byte)(newScanline1[x] * factor); - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + if (this.hasTrans && intensity == this.intensityTrans) + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 0)); + } + else + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + } + rowSpan[x] = color; } @@ -595,10 +726,10 @@ namespace ImageSharp.Formats for (int x = 0; x < this.header.Width; x++) { - int offset = 1 + (x * this.bytesPerPixel); + int offset = x * this.bytesPerPixel; - byte intensity = defilteredScanline[offset]; - byte alpha = defilteredScanline[offset + this.bytesPerSample]; + byte intensity = scanlineBuffer[offset]; + byte alpha = scanlineBuffer[offset + this.bytesPerSample]; color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha)); rowSpan[x] = color; @@ -608,25 +739,66 @@ namespace ImageSharp.Formats case PngColorType.Palette: - this.ProcessScanlineFromPalette(defilteredScanline, rowSpan); + this.ProcessScanlineFromPalette(scanlineBuffer, rowSpan); break; case PngColorType.Rgb: - if (this.header.BitDepth == 16) + if (!this.hasTrans) { - int length = this.header.Width * 3; - using (var compressed = new Buffer(length)) + if (this.header.BitDepth == 16) { - // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); - PixelOperations.Instance.PackFromRgb24Bytes(compressed, rowSpan, this.header.Width); + int length = this.header.Width * 3; + using (var compressed = new Buffer(length)) + { + // TODO: Should we use pack from vector here instead? + this.From16BitTo8Bit(scanlineBuffer, compressed, length); + PixelOperations.Instance.PackFromRgb24Bytes(compressed, rowSpan, this.header.Width); + } + } + else + { + PixelOperations.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width); } } else { - PixelOperations.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width); + if (this.header.BitDepth == 16) + { + int length = this.header.Width * 3; + using (var compressed = new Buffer(length)) + { + // TODO: Should we use pack from vector here instead? + this.From16BitTo8Bit(scanlineBuffer, compressed, length); + + Span rgb24Span = compressed.Span.NonPortableCast(); + for (int x = 0; x < this.header.Width; x++) + { + ref Rgb24 rgb24 = ref rgb24Span[x]; + var rgba32 = default(Rgba32); + rgba32.Rgb = rgb24; + rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255); + + color.PackFromRgba32(rgba32); + rowSpan[x] = color; + } + } + } + else + { + Span rgb24Span = scanlineBuffer.NonPortableCast(); + for (int x = 0; x < this.header.Width; x++) + { + ref Rgb24 rgb24 = ref rgb24Span[x]; + var rgba32 = default(Rgba32); + rgba32.Rgb = rgb24; + rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255); + + color.PackFromRgba32(rgba32); + rowSpan[x] = color; + } + } } break; @@ -667,16 +839,43 @@ namespace ImageSharp.Formats } } + /// + /// Decodes and assigns marker colors that identify transparent pixels in non indexed images + /// + /// The aplha tRNS array + private void AssignTransparentMarkers(byte[] alpha) + { + if (this.pngColorType == PngColorType.Rgb) + { + if (alpha.Length >= 6) + { + byte r = (byte)ReadIntFrom2Bytes(alpha, 0); + byte g = (byte)ReadIntFrom2Bytes(alpha, 2); + byte b = (byte)ReadIntFrom2Bytes(alpha, 4); + this.rgb24Trans = new Rgb24(r, g, b); + this.hasTrans = true; + } + } + else if (this.pngColorType == PngColorType.Grayscale) + { + if (alpha.Length >= 2) + { + this.intensityTrans = (byte)ReadIntFrom2Bytes(alpha, 0); + this.hasTrans = true; + } + } + } + /// /// Processes a scanline that uses a palette /// /// The type of pixel we are expanding to /// The scanline /// Thecurrent output image row - private void ProcessScanlineFromPalette(byte[] defilteredScanline, Span row) + private void ProcessScanlineFromPalette(Span defilteredScanline, Span row) where TPixel : struct, IPixel { - byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + Span newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] pal = this.palette; var color = default(TPixel); @@ -688,19 +887,11 @@ namespace ImageSharp.Formats // channel and we should try to read it. for (int x = 0; x < this.header.Width; x++) { - int index = newScanline[x + 1]; + int index = newScanline[x]; int pixelOffset = index * 3; rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; - - if (rgba.A > 0) - { - rgba.Rgb = pal.GetRgb24(pixelOffset); - } - else - { - rgba = default(Rgba32); - } + rgba.Rgb = pal.GetRgb24(pixelOffset); color.PackFromRgba32(rgba); row[x] = color; @@ -712,7 +903,7 @@ namespace ImageSharp.Formats for (int x = 0; x < this.header.Width; x++) { - int index = newScanline[x + 1]; + int index = newScanline[x]; int pixelOffset = index * 3; rgba.Rgb = pal.GetRgb24(pixelOffset); @@ -736,18 +927,27 @@ namespace ImageSharp.Formats { var color = default(TPixel); + // Trim the first marker byte from the buffer + var scanlineBuffer = new Span(defilteredScanline, 1); + switch (this.pngColorType) { case PngColorType.Grayscale: int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); - byte[] newScanline1 = ToArrayByBitsLength( - defilteredScanline, - this.bytesPerScanline, - this.header.BitDepth); - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) + Span newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); + + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) { byte intensity = (byte)(newScanline1[o] * factor); - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + if (this.hasTrans && intensity == this.intensityTrans) + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 0)); + } + else + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + } + rowSpan[x] = color; } @@ -755,10 +955,10 @@ namespace ImageSharp.Formats case PngColorType.GrayscaleWithAlpha: - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) { - byte intensity = defilteredScanline[o]; - byte alpha = defilteredScanline[o + this.bytesPerSample]; + byte intensity = scanlineBuffer[o]; + byte alpha = scanlineBuffer[o + this.bytesPerSample]; color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha)); rowSpan[x] = color; } @@ -767,31 +967,20 @@ namespace ImageSharp.Formats case PngColorType.Palette: - byte[] newScanline = ToArrayByBitsLength( - defilteredScanline, - this.bytesPerScanline, - this.header.BitDepth); + Span newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); var rgba = default(Rgba32); if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) { // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha // channel and we should try to read it. - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) { int index = newScanline[o]; int offset = index * 3; rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; - - if (rgba.A > 0) - { - rgba.Rgb = this.palette.GetRgb24(offset); - } - else - { - rgba = default(Rgba32); - } + rgba.Rgb = this.palette.GetRgb24(offset); color.PackFromRgba32(rgba); rowSpan[x] = color; @@ -801,7 +990,7 @@ namespace ImageSharp.Formats { rgba.A = 255; - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) { int index = newScanline[o]; int offset = index * 3; @@ -825,30 +1014,61 @@ namespace ImageSharp.Formats using (var compressed = new Buffer(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(new Span(defilteredScanline), compressed, length); - for (int x = pixelOffset, o = 1; - x < this.header.Width; - x += increment, o += this.bytesPerPixel) - { - rgba.R = compressed[o]; - rgba.G = compressed[o + this.bytesPerSample]; - rgba.B = compressed[o + (2 * this.bytesPerSample)]; + this.From16BitTo8Bit(scanlineBuffer, compressed, length); - color.PackFromRgba32(rgba); - rowSpan[x] = color; + if (this.hasTrans) + { + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) + { + rgba.R = compressed[o]; + rgba.G = compressed[o + 1]; + rgba.B = compressed[o + 2]; + rgba.A = (byte)(this.rgb24Trans.Equals(rgba.Rgb) ? 0 : 255); + + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } + } + else + { + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) + { + rgba.R = compressed[o]; + rgba.G = compressed[o + 1]; + rgba.B = compressed[o + 2]; + + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } } } } else { - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + if (this.hasTrans) { - rgba.R = defilteredScanline[o]; - rgba.G = defilteredScanline[o + this.bytesPerSample]; - rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + rgba.R = scanlineBuffer[o]; + rgba.G = scanlineBuffer[o + this.bytesPerSample]; + rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)]; + rgba.A = (byte)(this.rgb24Trans.Equals(rgba.Rgb) ? 0 : 255); - color.PackFromRgba32(rgba); - rowSpan[x] = color; + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } + } + else + { + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + rgba.R = scanlineBuffer[o]; + rgba.G = scanlineBuffer[o + this.bytesPerSample]; + rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)]; + + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } } } @@ -862,13 +1082,13 @@ namespace ImageSharp.Formats using (var compressed = new Buffer(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(new Span(defilteredScanline), compressed, length); - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + this.From16BitTo8Bit(scanlineBuffer, compressed, length); + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4) { rgba.R = compressed[o]; - rgba.G = compressed[o + this.bytesPerSample]; - rgba.B = compressed[o + (2 * this.bytesPerSample)]; - rgba.A = compressed[o + (3 * this.bytesPerSample)]; + rgba.G = compressed[o + 1]; + rgba.B = compressed[o + 2]; + rgba.A = compressed[o + 3]; color.PackFromRgba32(rgba); rowSpan[x] = color; @@ -877,12 +1097,12 @@ namespace ImageSharp.Formats } else { - for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) { - rgba.R = defilteredScanline[o]; - rgba.G = defilteredScanline[o + this.bytesPerSample]; - rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; - rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)]; + rgba.R = scanlineBuffer[o]; + rgba.G = scanlineBuffer[o + this.bytesPerSample]; + rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)]; + rgba.A = scanlineBuffer[o + (3 * this.bytesPerSample)]; color.PackFromRgba32(rgba); rowSpan[x] = color; @@ -985,12 +1205,23 @@ namespace ImageSharp.Formats { var chunk = new PngChunk(); this.ReadChunkLength(chunk); - if (chunk.Length < 0) + + if (chunk.Length == -1) { + // IEND return null; } + if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position) + { + // Not a valid chunk so we skip back all but one of the four bytes we have just read. + // That lets us read one byte at a time until we reach a known chunk. + this.currentStream.Position -= 3; + return chunk; + } + this.ReadChunkType(chunk); + if (chunk.Type == PngChunkTypes.Data) { return chunk; @@ -1025,12 +1256,21 @@ namespace ImageSharp.Formats this.crc.Update(this.chunkTypeBuffer); this.crc.Update(chunk.Data, 0, chunk.Length); - if (this.crc.Value != chunk.Crc) + if (this.crc.Value != chunk.Crc && IsCriticalChunk(chunk)) { - throw new ImageFormatException("CRC Error. PNG Image chunk is corrupt!"); + throw new ImageFormatException($"CRC Error. PNG {chunk.Type} chunk is corrupt!"); } } + /// + /// Skips the chunk data and the cycle redundancy chunk read from the data. + /// + private void SkipChunkDataAndCrc(PngChunk chunk) + { + this.currentStream.Skip(chunk.Length); + this.currentStream.Skip(4); + } + /// /// Reads the chunk data from the stream. /// @@ -1107,5 +1347,12 @@ namespace ImageSharp.Formats default: throw new ArgumentException($"Not a valid pass index: {passIndex}"); } } + + private void SwapBuffers() + { + Buffer temp = this.previousScanline; + this.previousScanline = this.scanline; + this.scanline = temp; + } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index bfd82a0748..4abafc9e80 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Image encoder for writing image data to a stream in png format. /// @@ -22,7 +18,7 @@ namespace ImageSharp.Formats public bool IgnoreMetadata { get; set; } /// - /// Gets or sets the size of the color palette to use. Set to zero to leav png encoding to use pixel data. + /// Gets or sets the size of the color palette to use. Set to zero to leave png encoding to use pixel data. /// public int PaletteSize { get; set; } = 0; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index cfbd0c4493..385d40b6ba 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1,23 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png.Filters; +using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; + +namespace SixLabors.ImageSharp.Formats.Png { - using System; - using System.Buffers; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - using Quantizers; - - using static ComparableExtensions; - /// /// Performs the png encoding operation. /// @@ -151,7 +147,7 @@ namespace ImageSharp.Formats /// /// Initializes a new instance of the class. /// - /// The options for influancing the encoder + /// The options for influencing the encoder public PngEncoderCore(IPngEncoderOptions options) { this.ignoreMetadata = options.IgnoreMetadata; @@ -168,7 +164,7 @@ namespace ImageSharp.Formats /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) where TPixel : struct, IPixel @@ -235,19 +231,17 @@ namespace ImageSharp.Formats // Collect the indexed pixel data if (this.pngColorType == PngColorType.Palette) { - this.CollectIndexedBytes(image, stream, header); + this.CollectIndexedBytes(image.Frames.RootFrame, stream, header); } this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - this.WriteDataChunks(image, stream); + this.WriteDataChunks(image.Frames.RootFrame, stream); this.WriteEndChunk(stream); stream.Flush(); } - /// - /// Disposes PngEncoderCore instance, disposing it's internal buffers. - /// + /// public void Dispose() { this.previousScanline?.Dispose(); @@ -306,7 +300,7 @@ namespace ImageSharp.Formats /// The image to encode. /// The containing image data. /// The . - private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header) + private void CollectIndexedBytes(ImageFrame image, Stream stream, PngHeader header) where TPixel : struct, IPixel { // Quantize the image and get the pixels. @@ -323,6 +317,7 @@ namespace ImageSharp.Formats where TPixel : struct, IPixel { byte[] rawScanlineArray = this.rawScanline.Array; + var rgba = default(Rgba32); // Copy the pixels across from the image. // Reuse the chunk type buffer. @@ -331,8 +326,8 @@ namespace ImageSharp.Formats // Convert the color to YCbCr and store the luminance // Optionally store the original color alpha. int offset = x * this.bytesPerPixel; - rowSpan[x].ToXyzwBytes(this.chunkTypeBuffer, 0); - byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2])); + rowSpan[x].ToRgba32(ref rgba); + byte luminance = (byte)((0.299F * rgba.R) + (0.587F * rgba.G) + (0.114F * rgba.B)); for (int i = 0; i < this.bytesPerPixel; i++) { @@ -342,7 +337,7 @@ namespace ImageSharp.Formats } else { - rawScanlineArray[offset + i] = this.chunkTypeBuffer[3]; + rawScanlineArray[offset + i] = rgba.A; } } } @@ -413,14 +408,12 @@ namespace ImageSharp.Formats // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. - UpFilter.Encode(scanSpan, prevSpan, this.up); + UpFilter.Encode(scanSpan, prevSpan, this.up, out int currentSum); - int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue); int lowestSum = currentSum; Buffer actualResult = this.up; - PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(this.paeth, currentSum); + PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel, out currentSum); if (currentSum < lowestSum) { @@ -428,8 +421,7 @@ namespace ImageSharp.Formats actualResult = this.paeth; } - SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue); + SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel, out currentSum); if (currentSum < lowestSum) { @@ -437,8 +429,7 @@ namespace ImageSharp.Formats actualResult = this.sub; } - AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel); - currentSum = this.CalculateTotalVariation(this.average, currentSum); + AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel, out currentSum); if (currentSum < lowestSum) { @@ -448,34 +439,6 @@ namespace ImageSharp.Formats return actualResult; } - /// - /// Calculates the total variation of given byte array. Total variation is the sum of the absolute values of - /// neighbor differences. - /// - /// The scanline bytes - /// The last variation sum - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int CalculateTotalVariation(Span scanline, int lastSum) - { - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - int sum = 0; - - for (int i = 1; i < this.bytesPerScanline; i++) - { - byte v = Unsafe.Add(ref scanBaseRef, i); - sum += v < 128 ? v : 256 - v; - - // No point continuing if we are larger. - if (sum >= lastSum) - { - break; - } - } - - return sum; - } - /// /// Calculates the correct number of bytes per pixel for the given color type. /// @@ -531,7 +494,7 @@ namespace ImageSharp.Formats /// The . /// The image to encode. /// The - private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) + private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageFrame image) where TPixel : struct, IPixel { if (this.paletteSize > 256) @@ -555,7 +518,7 @@ namespace ImageSharp.Formats int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); byte[] alphaTable = ArrayPool.Shared.Rent(pixelCount); - byte[] bytes = ArrayPool.Shared.Rent(4); + var rgba = default(Rgba32); bool anyAlpha = false; try { @@ -564,13 +527,13 @@ namespace ImageSharp.Formats if (quantized.Pixels.Contains(i)) { int offset = i * 3; - palette[i].ToXyzwBytes(bytes, 0); + palette[i].ToRgba32(ref rgba); - byte alpha = bytes[3]; + byte alpha = rgba.A; - colorTable[offset] = bytes[0]; - colorTable[offset + 1] = bytes[1]; - colorTable[offset + 2] = bytes[2]; + colorTable[offset] = rgba.R; + colorTable[offset + 1] = rgba.G; + colorTable[offset + 2] = rgba.B; if (alpha > this.threshold) { @@ -594,7 +557,6 @@ namespace ImageSharp.Formats { ArrayPool.Shared.Return(colorTable); ArrayPool.Shared.Return(alphaTable); - ArrayPool.Shared.Return(bytes); } return quantized; @@ -651,7 +613,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The image. /// The stream. - private void WriteDataChunks(Image pixels, Stream stream) + private void WriteDataChunks(ImageFrame pixels, Stream stream) where TPixel : struct, IPixel { this.bytesPerScanline = this.width * this.bytesPerPixel; @@ -679,10 +641,12 @@ namespace ImageSharp.Formats { for (int y = 0; y < this.height; y++) { - Buffer r = this.EncodePixelRow(pixels.GetRowSpan(y), y); + Buffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y); deflateStream.Write(r.Array, 0, resultLength); - Swap(ref this.rawScanline, ref this.previousScanline); + Buffer temp = this.rawScanline; + this.rawScanline = this.previousScanline; + this.previousScanline = temp; } } diff --git a/src/ImageSharp/Formats/Png/PngFormat.cs b/src/ImageSharp/Formats/Png/PngFormat.cs index 551b4a8c78..142f660712 100644 --- a/src/ImageSharp/Formats/Png/PngFormat.cs +++ b/src/ImageSharp/Formats/Png/PngFormat.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Png +{ /// - /// Registers the image encoders, decoders and mime type detectors for the jpeg format. + /// Registers the image encoders, decoders and mime type detectors for the png format. /// internal sealed class PngFormat : IImageFormat { diff --git a/src/ImageSharp/Formats/Png/PngHeader.cs b/src/ImageSharp/Formats/Png/PngHeader.cs index 9cf823fa19..a70032ce3c 100644 --- a/src/ImageSharp/Formats/Png/PngHeader.cs +++ b/src/ImageSharp/Formats/Png/PngHeader.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Represents the png header chunk. diff --git a/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs b/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs index fdea3eb8ae..837a147ed3 100644 --- a/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Detects png file headers /// diff --git a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs index b43aff0b78..10ebcc7bbe 100644 --- a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs +++ b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Provides enumeration of available PNG interlace modes. diff --git a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs index 5f92cc9e09..1cce90c0b7 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Computes Adler32 checksum for a stream of data. An Adler32 /// checksum is not as reliable as a CRC32 checksum, but a lot faster to @@ -76,9 +75,17 @@ namespace ImageSharp.Formats } /// - public long Value => this.checksum; + public long Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.checksum; + } + } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { this.checksum = 1; @@ -90,6 +97,7 @@ namespace ImageSharp.Formats /// /// The data value to add. The high byte of the int is ignored. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(int value) { // We could make a length 1 byte array and call update again, but I @@ -104,6 +112,7 @@ namespace ImageSharp.Formats } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(byte[] buffer) { if (buffer == null) @@ -115,32 +124,14 @@ namespace ImageSharp.Formats } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(byte[] buffer, int offset, int count) { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "cannot be negative"); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "cannot be negative"); - } - - if (offset >= buffer.Length) - { - throw new ArgumentOutOfRangeException(nameof(offset), "not a valid index into buffer"); - } - - if (offset + count > buffer.Length) - { - throw new ArgumentOutOfRangeException(nameof(count), "exceeds buffer size"); - } + DebugGuard.NotNull(buffer, nameof(buffer)); + DebugGuard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset)); + DebugGuard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); + DebugGuard.MustBeLessThan(offset, buffer.Length, nameof(offset)); + DebugGuard.MustBeLessThanOrEqualTo(offset + count, buffer.Length, nameof(count)); // (By Per Bothner) uint s1 = this.checksum & 0xFFFF; @@ -171,4 +162,4 @@ namespace ImageSharp.Formats this.checksum = (s2 << 16) | s1; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs index d940112a8d..bd686f2b9f 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. @@ -110,18 +109,15 @@ namespace ImageSharp.Formats /// public long Value { - get - { - return this.crc; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.crc; - set - { - this.crc = (uint)value; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => this.crc = (uint)value; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { this.crc = 0; @@ -131,6 +127,7 @@ namespace ImageSharp.Formats /// Updates the checksum with the given value. /// /// The byte is taken as the lower 8 bits of value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(int value) { this.crc ^= CrcSeed; @@ -139,6 +136,7 @@ namespace ImageSharp.Formats } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(byte[] buffer) { if (buffer == null) @@ -150,22 +148,13 @@ namespace ImageSharp.Formats } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(byte[] buffer, int offset, int count) { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be less than zero"); - } - - if (offset < 0 || offset + count > buffer.Length) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } + DebugGuard.NotNull(buffer, nameof(buffer)); + DebugGuard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); + DebugGuard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset)); + DebugGuard.MustBeLessThanOrEqualTo(offset + count, buffer.Length, nameof(count)); this.crc ^= CrcSeed; diff --git a/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs b/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs index cbd292dc4c..9d84258cae 100644 --- a/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs +++ b/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png.Zlib { /// /// Interface to compute a data checksum used by checked input/output streams. diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index c1f04fa981..dd20886ff7 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - using System.IO.Compression; +using System; +using System.IO; +using System.IO.Compression; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Provides methods and properties for compressing streams by using the Zlib Deflate algorithm. /// diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index 136f919da7..fd9c4ac63e 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -1,16 +1,24 @@ -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.IO.Compression; - using System.Text; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.IO.Compression; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Provides methods and properties for deframing streams from PNGs. /// internal sealed class ZlibInflateStream : Stream { + /// + /// Used to read the Adler-32 and Crc-32 checksums + /// We don't actually use this for anything so it doesn't + /// have to be threadsafe. + /// + private static readonly byte[] ChecksumBuffer = new byte[4]; + /// /// The inner raw memory stream /// @@ -35,9 +43,9 @@ private bool isDisposed; /// - /// The read crc data. + /// Whether the crc value has been read. /// - private byte[] crcread; + private bool crcRead; /// /// The current data remaining to be read @@ -146,14 +154,12 @@ this.compressedStream.Dispose(); this.compressedStream = null; - if (this.crcread == null) + if (!this.crcRead) { // Consume the trailing 4 bytes - this.crcread = new byte[4]; - for (int i = 0; i < 4; i++) - { - this.crcread[i] = (byte)this.innerStream.ReadByte(); - } + this.innerStream.Read(ChecksumBuffer, 0, 4); + this.currentDataRemaining -= 4; + this.crcRead = true; } } } @@ -168,11 +174,6 @@ private void InitializeInflateStream() { - // The DICT dictionary identifier identifying the used dictionary. - - // The preset dictionary. - bool fdict; - // Read the zlib header : http://tools.ietf.org/html/rfc1950 // CMF(Compression Method and flags) // This byte is divided into a 4 - bit compression method and a @@ -192,30 +193,35 @@ return; } - if ((cmf & 0x0f) != 8) + if ((cmf & 0x0F) == 8) { - throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); - } + // CINFO is the base-2 logarithm of the LZ77 window size, minus eight. + int cinfo = (cmf & 0xF0) >> 4; - // CINFO is the base-2 logarithm of the LZ77 window size, minus eight. - // int cinfo = ((cmf & (0xf0)) >> 8); - fdict = (flag & 32) != 0; + if (cinfo > 7) + { + // Values of CINFO above 7 are not allowed in RFC1950. + // CINFO is not defined in this specification for CM not equal to 8. + throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}"); + } + } + else + { + throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}"); + } + // The preset dictionary. + bool fdict = (flag & 32) != 0; if (fdict) { - // The DICT dictionary identifier identifying the used dictionary. - byte[] dictId = new byte[4]; - - for (int i = 0; i < 4; i++) - { - // We consume but don't use this. - dictId[i] = (byte)this.innerStream.ReadByte(); - this.currentDataRemaining--; - } + // We don't need this for inflate so simply skip by the next four bytes. + // https://tools.ietf.org/html/rfc1950#page-6 + this.innerStream.Read(ChecksumBuffer, 0, 4); + this.currentDataRemaining -= 4; } // Initialize the deflate Stream. this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index f45582e1e6..114eaab2a6 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Options for influencing the drawing functions. /// diff --git a/src/ImageSharp/Helpers/ImageExtensions.cs b/src/ImageSharp/Helpers/ImageExtensions.cs new file mode 100644 index 0000000000..dbf2e34a42 --- /dev/null +++ b/src/ImageSharp/Helpers/ImageExtensions.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Helpers +{ + /// + /// Extension methods over Image{TPixel} + /// + public static partial class ImageExtensions + { + /// + /// Gets the bounds of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Rectangle Bounds(this Image source) + where TPixel : struct, IPixel + => new Rectangle(0, 0, source.Width, source.Height); + + /// + /// Gets the bounds of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Rectangle Bounds(this ImageFrame source) + where TPixel : struct, IPixel + => new Rectangle(0, 0, source.Width, source.Height); + + /// + /// Gets the size of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Size Size(this Image source) + where TPixel : struct, IPixel + => new Size(source.Width, source.Height); + + /// + /// Gets the size of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Size Size(this ImageFrame source) + where TPixel : struct, IPixel + => new Size(source.Width, source.Height); + } +} diff --git a/src/ImageSharp/ICloningImageProcessor.cs b/src/ImageSharp/ICloningImageProcessor.cs new file mode 100644 index 0000000000..aeb3c815ec --- /dev/null +++ b/src/ImageSharp/ICloningImageProcessor.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. + /// + /// The pixel format. + internal interface ICloningImageProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Applies the process to the specified portion of the specified . + /// + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// + /// is null. + /// + /// + /// doesnt fit the dimension of the image. + /// + /// Returns the cloned image after thre processor has been applied to it. + Image CloneAndApply(Image source, Rectangle sourceRectangle); + } +} diff --git a/src/ImageSharp/IConfigurationModule.cs b/src/ImageSharp/IConfigurationModule.cs index 95b92ed056..93c40497da 100644 --- a/src/ImageSharp/IConfigurationModule.cs +++ b/src/ImageSharp/IConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Represents an interface that can register image encoders, decoders and image format detectors. diff --git a/src/ImageSharp/IImageProcessingContextFactory.cs b/src/ImageSharp/IImageProcessingContextFactory.cs new file mode 100644 index 0000000000..b7b935ecd5 --- /dev/null +++ b/src/ImageSharp/IImageProcessingContextFactory.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents an interface that will create IInternalImageProcessingContext instances + /// + internal interface IImageProcessingContextFactory + { + /// + /// Called during mutate operations to generate the image operations provider. + /// + /// The pixel format + /// The source image. + /// A flag to determine whether image operations are allowed to mutate the source image. + /// A new IImageOPeration + IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) + where TPixel : struct, IPixel; + } + + /// + /// The default implmentation of + /// + internal class DefaultImageOperationsProviderFactory : IImageProcessingContextFactory + { + /// + public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) + where TPixel : struct, IPixel + { + return new DefaultInternalImageProcessorContext(source, mutate); + } + } +} diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs new file mode 100644 index 0000000000..552e8d579d --- /dev/null +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// An interface to queue up image operations to apply to an image. + /// + /// The pixel format + public interface IImageProcessingContext + where TPixel : struct, IPixel + { + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply + /// The area to apply it to + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); + + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor); + } + + /// + /// An internal interface to queue up image operations and have a method to apply them to and return a result + /// + /// The pixel format + internal interface IInternalImageProcessingContext : IImageProcessingContext + where TPixel : struct, IPixel + { + /// + /// Adds the processors to the current image + /// + /// The current image or a new image depending on withere this is alloed to mutate the source image. + Image Apply(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageProcessor.cs b/src/ImageSharp/IImageProcessor.cs similarity index 54% rename from src/ImageSharp/Image/IImageProcessor.cs rename to src/ImageSharp/IImageProcessor.cs index 8687766d5e..bd6df8d835 100644 --- a/src/ImageSharp/Image/IImageProcessor.cs +++ b/src/ImageSharp/IImageProcessor.cs @@ -1,16 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Encapsulates methods to alter the pixels of an image. /// @@ -19,18 +14,7 @@ namespace ImageSharp.Processing where TPixel : struct, IPixel { /// - /// Gets or sets the parallel options for processing tasks in parallel. - /// - ParallelOptions ParallelOptions { get; set; } - - /// - /// Gets or sets a value indicating whether to compress - /// or expand individual pixel colors the value on processing. - /// - bool Compand { get; set; } - - /// - /// Applies the process to the specified portion of the specified . + /// Applies the process to the specified portion of the specified . /// /// The source image. Cannot be null. /// @@ -42,6 +26,6 @@ namespace ImageSharp.Processing /// /// doesnt fit the dimension of the image. /// - void Apply(ImageBase source, Rectangle sourceRectangle); + void Apply(Image source, Rectangle sourceRectangle); } } diff --git a/src/ImageSharp/IO/BigEndianBitConverter.cs b/src/ImageSharp/IO/BigEndianBitConverter.cs index debe1706c9..2fcfd966c3 100644 --- a/src/ImageSharp/IO/BigEndianBitConverter.cs +++ b/src/ImageSharp/IO/BigEndianBitConverter.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO +namespace SixLabors.ImageSharp.IO { /// /// Implementation of EndianBitConverter which converts to/from big-endian byte arrays. @@ -80,7 +78,7 @@ namespace ImageSharp.IO long p1 = (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3]; long p2 = (value[startIndex + 4] << 24) | (value[startIndex + 5] << 16) | (value[startIndex + 6] << 8) | value[startIndex + 7]; - return p2 | (p1 << 32); + return (p2 & 0xFFFFFFFF) | (p1 << 32); } } } \ No newline at end of file diff --git a/src/ImageSharp/IO/EndianBinaryReader.cs b/src/ImageSharp/IO/EndianBinaryReader.cs index e21d3d3db3..0d660c68d5 100644 --- a/src/ImageSharp/IO/EndianBinaryReader.cs +++ b/src/ImageSharp/IO/EndianBinaryReader.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.IO; - using System.Text; +using System; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness, depending on the it is constructed with. /// No data is buffered in the reader; the client may seek within the stream at will. diff --git a/src/ImageSharp/IO/EndianBinaryWriter.cs b/src/ImageSharp/IO/EndianBinaryWriter.cs index ef026f00c2..dd87faf455 100644 --- a/src/ImageSharp/IO/EndianBinaryWriter.cs +++ b/src/ImageSharp/IO/EndianBinaryWriter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.IO; - using System.Text; +using System; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness, depending on /// the it is constructed with. diff --git a/src/ImageSharp/IO/EndianBitConverter.Conversion.cs b/src/ImageSharp/IO/EndianBitConverter.Conversion.cs index 0858acfedc..844c81cc9e 100644 --- a/src/ImageSharp/IO/EndianBitConverter.Conversion.cs +++ b/src/ImageSharp/IO/EndianBitConverter.Conversion.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs b/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs index b46a453a4f..ea1d7aa5ac 100644 --- a/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs +++ b/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs b/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs index b3e0133e43..5686c829c4 100644 --- a/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs +++ b/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.ToType.cs b/src/ImageSharp/IO/EndianBitConverter.ToType.cs index 93b49558ad..0c0e49911b 100644 --- a/src/ImageSharp/IO/EndianBitConverter.ToType.cs +++ b/src/ImageSharp/IO/EndianBitConverter.ToType.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.cs b/src/ImageSharp/IO/EndianBitConverter.cs index 06b88dbc90..b563a09cb8 100644 --- a/src/ImageSharp/IO/EndianBitConverter.cs +++ b/src/ImageSharp/IO/EndianBitConverter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/Endianness.cs b/src/ImageSharp/IO/Endianness.cs index aefda6dc4b..59b2ae77c1 100644 --- a/src/ImageSharp/IO/Endianness.cs +++ b/src/ImageSharp/IO/Endianness.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO +namespace SixLabors.ImageSharp.IO { /// /// Endianness of a converter diff --git a/src/ImageSharp/IO/IFileSystem.cs b/src/ImageSharp/IO/IFileSystem.cs index ee1ef84d7b..088d4abb8b 100644 --- a/src/ImageSharp/IO/IFileSystem.cs +++ b/src/ImageSharp/IO/IFileSystem.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System.IO; +using System.IO; +namespace SixLabors.ImageSharp.IO +{ #if !NETSTANDARD1_1 /// /// A simple interface representing the filesystem. /// - public interface IFileSystem + internal interface IFileSystem { /// /// Returns a readable stream as defined by the path. diff --git a/src/ImageSharp/IO/LittleEndianBitConverter.cs b/src/ImageSharp/IO/LittleEndianBitConverter.cs index 81ed8d55c2..a69831586a 100644 --- a/src/ImageSharp/IO/LittleEndianBitConverter.cs +++ b/src/ImageSharp/IO/LittleEndianBitConverter.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO +namespace SixLabors.ImageSharp.IO { /// /// Implementation of EndianBitConverter which converts to/from little-endian byte arrays. @@ -77,7 +75,7 @@ namespace ImageSharp.IO CheckByteArgument(value, startIndex, 8); long p1 = (value[startIndex + 7] << 24) | (value[startIndex + 6] << 16) | (value[startIndex + 5] << 8) | value[startIndex + 4]; long p2 = (value[startIndex + 3] << 24) | (value[startIndex + 2] << 16) | (value[startIndex + 1] << 8) | value[startIndex]; - return p2 | (p1 << 32); + return (p2 & 0xFFFFFFFF) | (p1 << 32); } } } \ No newline at end of file diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs index 02a9914ead..204f5f4e1e 100644 --- a/src/ImageSharp/IO/LocalFileSystem.cs +++ b/src/ImageSharp/IO/LocalFileSystem.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.IO +{ #if !NETSTANDARD1_1 /// /// A wrapper around the local File apis. /// - public class LocalFileSystem : IFileSystem + internal class LocalFileSystem : IFileSystem { /// public Stream OpenRead(string path) diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs index b9e8e392a3..7355dc1fec 100644 --- a/src/ImageSharp/Image/IImage.cs +++ b/src/ImageSharp/Image/IImage.cs @@ -1,20 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { - using Formats; - /// - /// Encapsulates the basic properties and methods required to manipulate images. + /// Encapsulates the properties and methods that describe an image. /// - internal interface IImage : IImageBase + public interface IImage : IImageInfo { - /// - /// Gets the meta data of the image. - /// - ImageMetaData MetaData { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs deleted file mode 100644 index 0e087aa4a3..0000000000 --- a/src/ImageSharp/Image/IImageBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using SixLabors.Primitives; - - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - public interface IImageBase - { - /// - /// Gets the representing the bounds of the image. - /// - Rectangle Bounds { get; } - - /// - /// Gets the width in pixels. - /// - int Width { get; } - - /// - /// Gets the height in pixels. - /// - int Height { get; } - - /// - /// Gets the pixel ratio made up of the width and height. - /// - double PixelRatio { get; } - - /// - /// Gets the configuration providing initialization code which allows extending the library. - /// - Configuration Configuration { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase{TPixel}.cs b/src/ImageSharp/Image/IImageBase{TPixel}.cs deleted file mode 100644 index 8b4977b7dc..0000000000 --- a/src/ImageSharp/Image/IImageBase{TPixel}.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using ImageSharp.PixelFormats; - - /// - /// Encapsulates the basic properties and methods required to manipulate images in varying formats. - /// - /// The pixel format. - public interface IImageBase : IImageBase, IDisposable - where TPixel : struct, IPixel - { - /// - /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. - /// - Span Pixels { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageFrame.cs b/src/ImageSharp/Image/IImageFrame.cs deleted file mode 100644 index bf3261d93c..0000000000 --- a/src/ImageSharp/Image/IImageFrame.cs +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - internal interface IImageFrame : IImageBase - { - /// - /// Gets the meta data of the image. - /// - ImageFrameMetaData MetaData { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageFrameCollection.cs b/src/ImageSharp/Image/IImageFrameCollection.cs new file mode 100644 index 0000000000..81b512e221 --- /dev/null +++ b/src/ImageSharp/Image/IImageFrameCollection.cs @@ -0,0 +1,116 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections; +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Encapsulates an imaged collection of frames. + /// + /// The type of the pixel. + public interface IImageFrameCollection : IEnumerable> + where TPixel : struct, IPixel + { + /// + /// Gets the count. + /// + int Count { get; } + + /// + /// Gets the root frame. + /// + ImageFrame RootFrame { get; } + + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. + ImageFrame this[int index] { get; } + + /// + /// Clones the the frame at and generates a new images with all the same metadata from the orgional but with only the single frame on it. + /// + /// The zero-based index at which item should be removed. + /// Cannot remove last frame. + /// The new with only the one frame on it. + Image CloneFrame(int index); + + /// + /// Removed the frame at and generates a new images with all the same metadata from the orgional but with only the single frame on it. + /// + /// The zero-based index at which item should be removed. + /// Cannot remove last frame. + /// The new with only the one frame on it. + Image ExportFrame(int index); + + /// + /// Remove the frame at and frees all freeable resources associated with it. + /// + /// The zero-based index at which item should be removed. + /// Cannot remove last frame. + void RemoveFrame(int index); + + /// + /// Creates a new and appends it appends it to the end of the collection. + /// + /// The new . + ImageFrame CreateFrame(); + + /// + /// Clones the frame and appends the clone to the end of the collection. + /// + /// The raw pixel data to generate from. + /// The cloned . + ImageFrame AddFrame(ImageFrame source); + + /// + /// Creates a new frame from the pixel data at the same dimensions at the current image and inserts the new frame + /// into the at the end of the collection. + /// + /// The raw pixel data to generate from. + /// The new . + ImageFrame AddFrame(TPixel[] source); + + /// + /// Clones and inserts the into the at the specified . + /// + /// The zero-based index at which item should be inserted. + /// The to clone and insert into the . + /// Frame must have the same dimensions as the image - frame + /// The cloned . + ImageFrame InsertFrame(int index, ImageFrame source); + + /// + /// Moves a from the at the specified index to the other index. + /// + /// The zero-based index of the item to move. + /// The zero-based index of the new index that should be inserted at. + /// Cannot remove last frame. + void MoveFrame(int sourceIndex, int destinationIndex); + + /// + /// Determines the index of a specific in the . + /// + /// The to locate in the . + /// The index of item if found in the list; otherwise, -1. + int IndexOf(ImageFrame frame); + + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the the specified frame; otherwise, false. + /// + bool Contains(ImageFrame frame); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageInfo.cs b/src/ImageSharp/Image/IImageInfo.cs new file mode 100644 index 0000000000..25d5ec7cab --- /dev/null +++ b/src/ImageSharp/Image/IImageInfo.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; + +namespace SixLabors.ImageSharp +{ + /// + /// Encapsulates properties that descibe basic image information including dimensions, pixel type information + /// and additional metadata + /// + public interface IImageInfo + { + /// + /// Gets information about the image pixels. + /// + PixelTypeInfo PixelType { get; } + + /// + /// Gets the width. + /// + int Width { get; } + + /// + /// Gets the height. + /// + int Height { get; } + + /// + /// Gets the metadata of the image. + /// + ImageMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 1013107062..ef00ff15fd 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - using System.Linq; - using Formats; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the decoding of new images. /// @@ -81,5 +79,19 @@ namespace ImageSharp Image img = decoder.Decode(config, stream); return (img, format); } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The stream. + /// the configuration. + /// + /// The or null if suitable info detector not found. + /// + private static IImageInfo InternalIdentity(Stream stream, Configuration config) + { + var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector; + return detector?.Identify(config, stream); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromBytes.cs b/src/ImageSharp/Image/Image.FromBytes.cs index c7023b860b..9da9c5e432 100644 --- a/src/ImageSharp/Image/Image.FromBytes.cs +++ b/src/ImageSharp/Image/Image.FromBytes.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - using Formats; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from a byte array. /// diff --git a/src/ImageSharp/Image/Image.FromFile.cs b/src/ImageSharp/Image/Image.FromFile.cs index ca395ce088..0474a6d476 100644 --- a/src/ImageSharp/Image/Image.FromFile.cs +++ b/src/ImageSharp/Image/Image.FromFile.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ #if !NETSTANDARD1_1 - using System; - using System.IO; - using Formats; - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from a given file. /// @@ -217,5 +215,5 @@ namespace ImageSharp } } } -#endif -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 29d93ae859..62668dd023 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from a given stream. /// @@ -39,6 +36,37 @@ namespace ImageSharp return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default)); } + /// + /// By reading the header on the provided stream this reads the raw image information. + /// + /// The image stream to read the header from. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(Stream stream) + { + return Identify(null, stream); + } + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The configuration. + /// The image stream to read the information from. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// + /// The or null if suitable info detector is not found. + /// + public static IImageInfo Identify(Configuration config, Stream stream) + { + return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default)); + } + /// /// Create a new instance of the class from the given stream. /// diff --git a/src/ImageSharp/Image/Image.LoadPixelData.cs b/src/ImageSharp/Image/Image.LoadPixelData.cs index 7b6a4d6684..5f1a1617f2 100644 --- a/src/ImageSharp/Image/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image/Image.LoadPixelData.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using Formats; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from raw pixel data. /// @@ -26,7 +25,19 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Span data, int width, int height) + public static Image LoadPixelData(TPixel[] data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(Configuration.Default, data, width, height); + + /// + /// Create a new instance of the class from the raw data. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + private static Image LoadPixelData(Span data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(Configuration.Default, data, width, height); @@ -38,10 +49,35 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Span data, int width, int height) + public static Image LoadPixelData(byte[] data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(Configuration.Default, data, width, height); + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + private static Image LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(Configuration.Default, data, width, height); + + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static Image LoadPixelData(Configuration config, byte[] data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(config, new Span(data).NonPortableCast(), width, height); + /// /// Create a new instance of the class from the given byte array in format. /// @@ -51,7 +87,7 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Configuration config, Span data, int width, int height) + private static Image LoadPixelData(Configuration config, Span data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(config, data.NonPortableCast(), width, height); @@ -64,14 +100,35 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Configuration config, Span data, int width, int height) + public static Image LoadPixelData(Configuration config, TPixel[] data, int width, int height) + where TPixel : struct, IPixel + { + int count = width * height; + Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); + + var image = new Image(config, width, height); + SpanHelper.Copy(data, image.GetPixelSpan(), count); + + return image; + } + + /// + /// Create a new instance of the class from the raw data. + /// + /// The config for the decoder. + /// The Span containing the image Pixel data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + private static Image LoadPixelData(Configuration config, Span data, int width, int height) where TPixel : struct, IPixel { int count = width * height; Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); var image = new Image(config, width, height); - SpanHelper.Copy(data, image.Pixels, count); + SpanHelper.Copy(data, image.Frames.RootFrame.GetPixelSpan(), count); return image; } diff --git a/src/ImageSharp/Image/ImageBase{TPixel}.cs b/src/ImageSharp/Image/ImageBase{TPixel}.cs deleted file mode 100644 index 20b891f2dd..0000000000 --- a/src/ImageSharp/Image/ImageBase{TPixel}.cs +++ /dev/null @@ -1,365 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - - /// - /// The base class of all images. Encapsulates the basic properties and methods required to manipulate - /// images in different pixel formats. - /// - /// The pixel format. - [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract class ImageBase : IImageBase - where TPixel : struct, IPixel - { - /// - /// Gets or sets the maximum allowable width in pixels. - /// - public const int MaxWidth = int.MaxValue; - - /// - /// Gets or sets the maximum allowable height in pixels. - /// - public const int MaxHeight = int.MaxValue; - -#pragma warning disable SA1401 // Fields must be private - /// - /// The image pixels. Not private as Buffer2D requires an array in its constructor. - /// - internal TPixel[] PixelBuffer; -#pragma warning restore SA1401 // Fields must be private - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// If the entity is disposed, it must not be disposed a second time. The isDisposed field is set the first time the entity - /// is disposed. If the isDisposed field is true, then the Dispose() method will not dispose again. This help not to prolong the entity's - /// life in the Garbage Collector. - /// - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - protected ImageBase(Configuration configuration) - { - this.Configuration = configuration ?? Configuration.Default; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// The width of the image in pixels. - /// The height of the image in pixels. - /// - /// Thrown if either or are less than or equal to 0. - /// - protected ImageBase(Configuration configuration, int width, int height) - : this(configuration) - { - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - this.Width = width; - this.Height = height; - this.RentPixels(); - this.ClearPixels(); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The other to create this instance from. - /// - /// - /// Thrown if the given is null. - /// - protected ImageBase(ImageBase other) - : this(other.Configuration) - { - Guard.NotNull(other, nameof(other), "Other image cannot be null."); - - this.Width = other.Width; - this.Height = other.Height; - this.CopyProperties(other); - - // Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. - this.RentPixels(); - using (PixelAccessor sourcePixels = other.Lock()) - using (PixelAccessor target = this.Lock()) - { - // Check we can do this without crashing - sourcePixels.CopyTo(target); - } - } - - /// - public Span Pixels => new Span(this.PixelBuffer, 0, this.Width * this.Height); - - /// - public int Width { get; private set; } - - /// - public int Height { get; private set; } - - /// - public double PixelRatio => (double)this.Width / this.Height; - - /// - public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - - /// - /// Gets the configuration providing initialization code which allows extending the library. - /// - public Configuration Configuration { get; private set; } - - /// - /// Gets or sets the pixel at the specified position. - /// - /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - public TPixel this[int x, int y] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - this.CheckCoordinates(x, y); - return this.PixelBuffer[(y * this.Width) + x]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.CheckCoordinates(x, y); - this.PixelBuffer[(y * this.Width) + x] = value; - } - } - - /// - /// Gets a reference to the pixel at the specified position. - /// - /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TPixel GetPixelReference(int x, int y) - { - this.CheckCoordinates(x, y); - return ref this.PixelBuffer[(y * this.Width) + x]; - } - - /// - /// Gets a representing the row 'y' beginning from the the first pixel on that row. - /// - /// The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the image. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int y) - { - this.CheckCoordinates(y); - return this.Pixels.Slice(y * this.Width, this.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. - /// - /// The x coordinate (position in the row) - /// The y (row) coordinate - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int x, int y) - { - this.CheckCoordinates(x, y); - return this.Pixels.Slice((y * this.Width) + x, this.Width - x); - } - - /// - /// Applies the processor. - /// - /// The processor. - /// The rectangle. - public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - processor.Apply(this, rectangle); - } - - /// - public void Dispose() - { - this.Dispose(true); - - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - - /// - /// Locks the image providing access to the pixels. - /// - /// It is imperative that the accessor is correctly disposed off after use. - /// - /// - /// The - internal PixelAccessor Lock() - { - return new PixelAccessor(this); - } - - /// - /// Copies the pixels to another of the same size. - /// - /// The target pixel buffer accessor. - internal void CopyTo(PixelAccessor target) - { - SpanHelper.Copy(this.Pixels, target.PixelBuffer.Span); - } - - /// - /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer. - /// - /// The pixel source. - internal void SwapPixelsBuffers(PixelAccessor pixelSource) - { - Guard.NotNull(pixelSource, nameof(pixelSource)); - - int newWidth = pixelSource.Width; - int newHeight = pixelSource.Height; - - // Push my memory into the accessor (which in turn unpins the old buffer ready for the images use) - TPixel[] newPixels = pixelSource.ReturnCurrentColorsAndReplaceThemInternally(this.Width, this.Height, this.PixelBuffer); - this.Width = newWidth; - this.Height = newHeight; - this.PixelBuffer = newPixels; - } - - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - protected void CopyProperties(IImageBase other) - { - DebugGuard.NotNull(other, nameof(other)); - - this.Configuration = other.Configuration; - } - - /// - /// Releases any unmanaged resources from the inheriting class. - /// - protected virtual void ReleaseUnmanagedResources() - { - // TODO release unmanaged resources here - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - protected virtual void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - this.ReleaseUnmanagedResources(); - - if (disposing) - { - this.ReturnPixels(); - } - - // Note disposing is done. - this.isDisposed = true; - } - - /// - /// Rents the pixel array from the pool. - /// - private void RentPixels() - { - this.PixelBuffer = PixelDataPool.Rent(this.Width * this.Height); - } - - /// - /// Returns the rented pixel array back to the pool. - /// - private void ReturnPixels() - { - PixelDataPool.Return(this.PixelBuffer); - this.PixelBuffer = null; - } - - /// - /// Clears the pixel array. - /// - private void ClearPixels() - { - Array.Clear(this.PixelBuffer, 0, this.Width * this.Height); - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int y) - { - if (y < 0 || y >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds."); - } - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The x-coordinate of the pixel. Must be greater than zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int x, int y) - { - if (x < 0 || x >= this.Width) - { - throw new ArgumentOutOfRangeException(nameof(x), x, $"{x} is outwith the image bounds."); - } - - if (y < 0 || y >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds."); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs new file mode 100644 index 0000000000..c4de1c2988 --- /dev/null +++ b/src/ImageSharp/Image/ImageExtensions.cs @@ -0,0 +1,226 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods over Image{TPixel} + /// + public static partial class ImageExtensions + { +#if !NETSTANDARD1_1 + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The Pixel format. + /// The source image + /// The file path to save the image to. + /// Thrown if the stream is null. + public static void Save(this Image source, string filePath) + where TPixel : struct, IPixel + { + Guard.NotNullOrEmpty(filePath, nameof(filePath)); + + string ext = Path.GetExtension(filePath).Trim('.'); + IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext); + if (format == null) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); + foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) + { + stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + + IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + + if (encoder == null) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); + foreach (KeyValuePair enc in source.GetConfiguration().ImageEncoders) + { + stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + + source.Save(filePath, encoder); + } + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The Pixel format. + /// The source image + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the encoder is null. + public static void Save(this Image source, string filePath, IImageEncoder encoder) + where TPixel : struct, IPixel + { + Guard.NotNull(encoder, nameof(encoder)); + using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath)) + { + source.Save(fs, encoder); + } + } +#endif + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The Pixel format. + /// The source image + /// The stream to save the image to. + /// The format to save the image to. + /// Thrown if the stream is null. + public static void Save(this Image source, Stream stream, IImageFormat format) + where TPixel : struct, IPixel + { + Guard.NotNull(format, nameof(format)); + IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + + if (encoder == null) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); + + foreach (KeyValuePair val in source.GetConfiguration().ImageEncoders) + { + stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + + source.Save(stream, encoder); + } + + /// + /// Saves the raw image pixels to a byte array in row-major order. + /// + /// The Pixel format. + /// The source image + /// A copy of the pixel data as bytes from this frame. + /// Thrown if the stream is null. + public static byte[] SavePixelData(this ImageFrame source) + where TPixel : struct, IPixel + => source.GetPixelSpan().AsBytes().ToArray(); + + /// + /// Saves the raw image pixels to the given byte array in row-major order. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this ImageFrame source, byte[] buffer) + where TPixel : struct, IPixel + => SavePixelData(source, buffer.AsSpan().NonPortableCast()); + + /// + /// Saves the raw image pixels to the given TPixel array in row-major order. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this ImageFrame source, TPixel[] buffer) + where TPixel : struct, IPixel + => SavePixelData(source, new Span(buffer)); + + /// + /// Saves the raw image pixels to a byte array in row-major order. + /// + /// The Pixel format. + /// The source image + /// A copy of the pixel data from the first frame as bytes. + /// Thrown if the stream is null. + public static byte[] SavePixelData(this Image source) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(); + + /// + /// Saves the raw image pixels to the given byte array in row-major order. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this Image source, byte[] buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer); + + /// + /// Saves the raw image pixels to the given TPixel array in row-major order. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this Image source, TPixel[] buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer); + + /// + /// Returns a Base64 encoded string from the given image. + /// + /// + /// The Pixel format. + /// The source image + /// The format. + /// The + public static string ToBase64String(this Image source, IImageFormat format) + where TPixel : struct, IPixel + { + using (var stream = new MemoryStream()) + { + source.Save(stream, format); + stream.Flush(); + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; + } + } + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + internal static void SavePixelData(this Image source, Span buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer.NonPortableCast()); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + internal static void SavePixelData(this ImageFrame source, Span buffer) + where TPixel : struct, IPixel + { + Span sourceBuffer = source.GetPixelSpan(); + Guard.MustBeGreaterThanOrEqualTo(buffer.Length, sourceBuffer.Length, nameof(buffer)); + + sourceBuffer.CopyTo(buffer); + } + } +} diff --git a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs new file mode 100644 index 0000000000..e2230c4367 --- /dev/null +++ b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Adds static methods allowing the creation of new image from raw pixel data. + /// + internal static partial class ImageFrame + { + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static ImageFrame LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(data.NonPortableCast(), width, height); + + /// + /// Create a new instance of the class from the raw data. + /// + /// The Span containing the image Pixel data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static ImageFrame LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + { + int count = width * height; + Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); + + var image = new ImageFrame(width, height); + SpanHelper.Copy(data, image.GetPixelSpan(), count); + + return image; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs new file mode 100644 index 0000000000..3e9bb03435 --- /dev/null +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -0,0 +1,180 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections; +using System.Collections.Generic; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Encapsulates an imaged collection of frames. + /// + /// The type of the pixel. + internal sealed class ImageFrameCollection : IImageFrameCollection + where TPixel : struct, IPixel + { + private readonly IList> frames = new List>(); + private readonly Image parent; + + internal ImageFrameCollection(Image parent, int width, int height) + { + Guard.NotNull(parent, nameof(parent)); + + this.parent = parent; + + // Frames are already cloned within the caller + this.frames.Add(new ImageFrame(width, height)); + } + + internal ImageFrameCollection(Image parent, IEnumerable> frames) + { + Guard.NotNull(parent, nameof(parent)); + Guard.NotNullOrEmpty(frames, nameof(frames)); + + this.parent = parent; + + // Frames are already cloned by the caller + foreach (ImageFrame f in frames) + { + this.ValidateFrame(f); + this.frames.Add(f); + } + } + + /// + public int Count => this.frames.Count; + + /// + public ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null; + + /// + public ImageFrame this[int index] + { + get => this.frames[index]; + } + + /// + public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame); + + /// + public ImageFrame InsertFrame(int index, ImageFrame frame) + { + this.ValidateFrame(frame); + ImageFrame clonedFrame = frame.Clone(); + this.frames.Insert(index, clonedFrame); + return clonedFrame; + } + + /// + public ImageFrame AddFrame(ImageFrame frame) + { + this.ValidateFrame(frame); + ImageFrame clonedFrame = frame.Clone(); + this.frames.Add(clonedFrame); + return clonedFrame; + } + + /// + public ImageFrame AddFrame(TPixel[] data) + { + var frame = ImageFrame.LoadPixelData(new Span(data), this.RootFrame.Width, this.RootFrame.Height); + this.frames.Add(frame); + return frame; + } + + /// + public void RemoveFrame(int index) + { + if (index == 0 && this.Count == 1) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + ImageFrame frame = this.frames[index]; + this.frames.RemoveAt(index); + frame.Dispose(); + } + + /// + public bool Contains(ImageFrame frame) + { + return this.frames.Contains(frame); + } + + /// + public void MoveFrame(int sourceIndex, int destIndex) + { + if (sourceIndex == destIndex) + { + return; + } + + ImageFrame frameAtIndex = this.frames[sourceIndex]; + this.frames.RemoveAt(sourceIndex); + this.frames.Insert(destIndex, frameAtIndex); + } + + /// + public Image ExportFrame(int index) + { + ImageFrame frame = this[index]; + + if (this.Count == 1 && this.frames.Contains(frame)) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + this.frames.Remove(frame); + + return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame }); + } + + /// + public Image CloneFrame(int index) + { + ImageFrame frame = this[index]; + ImageFrame clonedFrame = frame.Clone(); + return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame }); + } + + /// + public ImageFrame CreateFrame() + { + var frame = new ImageFrame(this.RootFrame.Width, this.RootFrame.Height); + this.frames.Add(frame); + return frame; + } + + /// + IEnumerator> IEnumerable>.GetEnumerator() => this.frames.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator(); + + private void ValidateFrame(ImageFrame frame) + { + Guard.NotNull(frame, nameof(frame)); + + if (this.Count != 0) + { + if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height) + { + throw new ArgumentException("Frame must have the same dimensions as the image.", nameof(frame)); + } + } + } + + internal void Dispose() + { + foreach (ImageFrame f in this.frames) + { + f.Dispose(); + } + + this.frames.Clear(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 04b9902c67..ba475f9cf3 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -1,78 +1,217 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Represents a single frame in a animation. /// /// The pixel format. - public class ImageFrame : ImageBase, IImageFrame + public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// The image pixels. Not private as Buffer2D requires an array in its constructor. + /// + private Buffer2D pixelBuffer; + + private bool isDisposed; + + /// + /// Initializes a new instance of the class. /// /// The width of the image in pixels. /// The height of the image in pixels. - /// - /// The configuration providing initialization code which allows extending the library. - /// - public ImageFrame(int width, int height, Configuration configuration = null) - : base(configuration, width, height) + internal ImageFrame(int width, int height) + : this(width, height, new ImageFrameMetaData()) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The image to create the frame from. - public ImageFrame(ImageFrame image) - : base(image) + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The meta data. + internal ImageFrame(int width, int height, ImageFrameMetaData metaData) { - this.CopyProperties(image); + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + Guard.NotNull(metaData, nameof(metaData)); + + this.pixelBuffer = Buffer2D.CreateClean(width, height); + this.MetaData = metaData; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The image to create the frame from. - public ImageFrame(ImageBase image) - : base(image) + /// The of the frame. + /// The meta data. + internal ImageFrame(Size size, ImageFrameMetaData metaData) + : this(size.Width, size.Height, metaData) { } + /// + /// Initializes a new instance of the class. + /// + /// The source. + internal ImageFrame(ImageFrame source) + { + this.pixelBuffer = new Buffer2D(source.pixelBuffer.Width, source.pixelBuffer.Height); + source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span); + this.MetaData = source.MetaData.Clone(); + } + + /// + Buffer2D IPixelSource.PixelBuffer => this.pixelBuffer; + + /// + /// Gets the width. + /// + public int Width => this.pixelBuffer.Width; + + /// + /// Gets the height. + /// + public int Height => this.pixelBuffer.Height; + /// /// Gets the meta data of the frame. /// - public ImageFrameMetaData MetaData { get; private set; } = new ImageFrameMetaData(); + public ImageFrameMetaData MetaData { get; private set; } + + /// + /// Gets or sets the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + public TPixel this[int x, int y] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.pixelBuffer[x, y]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.pixelBuffer[x, y] = value; + } + } + + /// + /// Gets a reference to the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref TPixel GetPixelReference(int x, int y) + { + return ref this.pixelBuffer[x, y]; + } + + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The + internal PixelAccessor Lock() + { + return new PixelAccessor(this); + } + + /// + /// Copies the pixels to another of the same size. + /// + /// The target pixel buffer accessor. + internal void CopyTo(PixelAccessor target) + { + SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span); + } + + /// + /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer. + /// + /// The pixel source. + internal void SwapPixelsBuffers(PixelAccessor pixelSource) + { + Guard.NotNull(pixelSource, nameof(pixelSource)); + + // Push my memory into the accessor (which in turn unpins the old buffer ready for the images use) + Buffer2D newPixels = pixelSource.SwapBufferOwnership(this.pixelBuffer); + this.pixelBuffer = newPixels; + } + + /// + /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer. + /// + /// The pixel source. + internal void SwapPixelsBuffers(ImageFrame pixelSource) + { + Guard.NotNull(pixelSource, nameof(pixelSource)); + + Buffer2D temp = this.pixelBuffer; + this.pixelBuffer = pixelSource.pixelBuffer; + pixelSource.pixelBuffer = temp; + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + internal void Dispose() + { + if (this.isDisposed) + { + return; + } + + this.pixelBuffer?.Dispose(); + this.pixelBuffer = null; + + // Note disposing is done. + this.isDisposed = true; + } /// public override string ToString() { - return $"ImageFrame: {this.Width}x{this.Height}"; + return $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; } /// /// Returns a copy of the image frame in the given pixel format. /// - /// A function that allows for the correction of vector scaling between unknown color formats. /// The pixel format. /// The - public ImageFrame To(Func scaleFunc = null) + internal ImageFrame CloneAs() where TPixel2 : struct, IPixel { - scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); + if (typeof(TPixel2) == typeof(TPixel)) + { + return this.Clone() as ImageFrame; + } + + Func scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(); - ImageFrame target = new ImageFrame(this.Width, this.Height, this.Configuration); - target.CopyProperties(this); + var target = new ImageFrame(this.Width, this.Height, this.MetaData.Clone()); using (PixelAccessor pixels = this.Lock()) using (PixelAccessor targetPixels = target.Lock()) @@ -80,12 +219,12 @@ namespace ImageSharp Parallel.For( 0, target.Height, - this.Configuration.ParallelOptions, + Configuration.Default.ParallelOptions, y => { for (int x = 0; x < target.Width; x++) { - TPixel2 color = default(TPixel2); + var color = default(TPixel2); color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); targetPixels[x, y] = color; } @@ -99,22 +238,15 @@ namespace ImageSharp /// Clones the current instance. /// /// The - internal virtual ImageFrame Clone() + internal ImageFrame Clone() { return new ImageFrame(this); } - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - private void CopyProperties(IImageFrame other) + /// + void IDisposable.Dispose() { - base.CopyProperties(other); - - this.MetaData = new ImageFrameMetaData(other.MetaData); + this.Dispose(); } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageInfo.cs b/src/ImageSharp/Image/ImageInfo.cs new file mode 100644 index 0000000000..6f894cb599 --- /dev/null +++ b/src/ImageSharp/Image/ImageInfo.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; + +namespace SixLabors.ImageSharp +{ + /// + /// Contains information about the image including dimensions, pixel type information and additional metadata + /// + internal sealed class ImageInfo : IImageInfo + { + /// + /// Initializes a new instance of the class. + /// + /// The image pixel type information. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The images metadata. + public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData) + { + this.PixelType = pixelType; + this.Width = width; + this.Height = height; + this.MetaData = metaData; + } + + /// + public PixelTypeInfo PixelType { get; } + + /// + public int Width { get; } + + /// + public int Height { get; } + + /// + public ImageMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs deleted file mode 100644 index 8eed103d10..0000000000 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Applies the processor to the image. - /// This method does not resize the target image. - /// - /// The pixel format. - /// The image this method extends. - /// The processor to apply to the image. - /// The . - public static Image Apply(this Image source, IImageProcessor processor) - where TPixel : struct, IPixel - { - source.ApplyProcessor(processor, source.Bounds); - return source; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 5e8bcab31b..be38b41f24 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -1,31 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp { - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Numerics; - using System.Text; - using System.Threading.Tasks; - - using Formats; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - [DebuggerDisplay("Image: {Width}x{Height}")] - public class Image : ImageBase, IImage + public sealed partial class Image : IImage, IDisposable, IConfigurable where TPixel : struct, IPixel { + private Configuration configuration; + private ImageFrameCollection frames; + /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -51,37 +48,6 @@ namespace ImageSharp { } - /// - /// Initializes a new instance of the class - /// by making a copy from another image. - /// - /// The other image, where the clone should be made from. - /// is null. - public Image(Image other) - : base(other) - { - foreach (ImageFrame frame in other.Frames) - { - if (frame != null) - { - this.Frames.Add(new ImageFrame(frame)); - } - } - - this.CopyProperties(other); - } - - /// - /// Initializes a new instance of the class - /// by making a copy from another image. - /// - /// The other image, where the clone should be made from. - /// is null. - public Image(ImageBase other) - : base(other) - { - } - /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -93,87 +59,67 @@ namespace ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) - : base(configuration, width, height) { + this.configuration = configuration ?? Configuration.Default; + this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.MetaData = metadata ?? new ImageMetaData(); + this.frames = new ImageFrameCollection(this, width, height); } /// - /// Gets the meta data of the image. + /// Initializes a new instance of the class + /// with the height and the width of the image. /// - public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); + /// The configuration providing initialization code which allows extending the library. + /// The images metadata. + /// The frames that will be owned by this image instance. + internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames) + { + this.configuration = configuration ?? Configuration.Default; + this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); + this.MetaData = metadata ?? new ImageMetaData(); - /// - /// Gets the width of the image in inches. It is calculated as the width of the image - /// in pixels multiplied with the density. When the density is equals or less than zero - /// the default value is used. - /// - /// The width of the image in inches. - public double InchWidth => this.Width / this.MetaData.HorizontalResolution; + this.frames = new ImageFrameCollection(this, frames); + } /// - /// Gets the height of the image in inches. It is calculated as the height of the image - /// in pixels multiplied with the density. When the density is equals or less than zero - /// the default value is used. + /// Gets the pixel buffer. /// - /// The height of the image in inches. - public double InchHeight => this.Height / this.MetaData.VerticalResolution; + Configuration IConfigurable.Configuration => this.configuration; - /// - /// Gets a value indicating whether this image is animated. - /// - /// - /// True if this image is animated; otherwise, false. - /// - public bool IsAnimated => this.Frames.Count > 0; + /// + public PixelTypeInfo PixelType { get; } + + /// + public int Width => this.frames.RootFrame.Width; + + /// + public int Height => this.frames.RootFrame.Height; + + /// + public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); /// - /// Gets the other frames for the animation. + /// Gets the frames. /// - /// The list of frame images. - public IList> Frames { get; } = new List>(); + public IImageFrameCollection Frames => this.frames; /// - /// Applies the processor to the image. + /// Gets the root frame. /// - /// The processor to apply to the image. - /// The structure that specifies the portion of the image object to draw. - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - // we want to put this on on here as it gives us a really go place to test/verify processor settings - base.ApplyProcessor(processor, rectangle); - foreach (ImageFrame sourceFrame in this.Frames) - { - sourceFrame.ApplyProcessor(processor, rectangle); - } - } + private IPixelSource PixelSource => this.frames?.RootFrame ?? throw new ObjectDisposedException(nameof(Image)); /// - /// Saves the image to the given stream using the currently loaded image format. + /// Gets or sets the pixel at the specified position. /// - /// The stream to save the image to. - /// The format to save the image to. - /// Thrown if the stream is null. - /// The - public Image Save(Stream stream, IImageFormat format) + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + public TPixel this[int x, int y] { - Guard.NotNull(format, nameof(format)); - IImageEncoder encoder = this.Configuration.FindEncoder(format); - - if (encoder == null) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); - - foreach (KeyValuePair val in this.Configuration.ImageEncoders) - { - stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); - } - - throw new NotSupportedException(stringBuilder.ToString()); - } + get => this.PixelSource.PixelBuffer[x, y]; - return this.Save(stream, encoder); + set => this.PixelSource.PixelBuffer[x, y] = value; } /// @@ -182,170 +128,64 @@ namespace ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream or encoder is null. - /// - /// The . - /// - public Image Save(Stream stream, IImageEncoder encoder) + public void Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); encoder.Encode(this, stream); - - return this; } -#if !NETSTANDARD1_1 /// - /// Saves the image to the given stream using the currently loaded image format. + /// Clones the current image /// - /// The file path to save the image to. - /// Thrown if the stream is null. - /// The - public Image Save(string filePath) + /// Returns a new image with all the same metadata as the original. + public Image Clone() { - Guard.NotNullOrEmpty(filePath, nameof(filePath)); - - string ext = Path.GetExtension(filePath).Trim('.'); - var format = this.Configuration.FindFormatByFileExtensions(ext); - if (format == null) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); - foreach (IImageFormat fmt in this.Configuration.ImageFormats) - { - stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); - } - - throw new NotSupportedException(stringBuilder.ToString()); - } - - IImageEncoder encoder = this.Configuration.FindEncoder(format); - - if (encoder == null) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); - foreach (KeyValuePair enc in this.Configuration.ImageEncoders) - { - stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); - } - - throw new NotSupportedException(stringBuilder.ToString()); - } - - return this.Save(filePath, encoder); + IEnumerable> clonedFrames = this.frames.Select(x => x.Clone()); + return new Image(this.configuration, this.MetaData.Clone(), clonedFrames); } - /// - /// Saves the image to the given stream using the currently loaded image format. - /// - /// The file path to save the image to. - /// The encoder to save the image with. - /// Thrown if the encoder is null. - /// The - public Image Save(string filePath, IImageEncoder encoder) - { - Guard.NotNull(encoder, nameof(encoder)); - using (Stream fs = this.Configuration.FileSystem.Create(filePath)) - { - return this.Save(fs, encoder); - } - } -#endif - /// public override string ToString() { return $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; } - /// - /// Returns a Base64 encoded string from the given image. - /// - /// - /// The format. - /// The - public string ToBase64String(IImageFormat format) - { - using (var stream = new MemoryStream()) - { - this.Save(stream, format); - stream.Flush(); - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; - } - } - /// /// Returns a copy of the image in the given pixel format. /// - /// A function that allows for the correction of vector scaling between unknown color formats. /// The pixel format. /// The - public Image To(Func scaleFunc = null) + public Image CloneAs() where TPixel2 : struct, IPixel { - scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); - - var target = new Image(this.Configuration, this.Width, this.Height); - target.CopyProperties(this); - - using (PixelAccessor pixels = this.Lock()) - using (PixelAccessor targetPixels = target.Lock()) - { - Parallel.For( - 0, - target.Height, - this.Configuration.ParallelOptions, - y => - { - for (int x = 0; x < target.Width; x++) - { - var color = default(TPixel2); - color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); - targetPixels[x, y] = color; - } - }); - } - - for (int i = 0; i < this.Frames.Count; i++) - { - target.Frames.Add(this.Frames[i].To()); - } + IEnumerable> clonedFrames = this.frames.Select(x => x.CloneAs()); + var target = new Image(this.configuration, this.MetaData.Clone(), clonedFrames); return target; } /// - /// Creates a new from this instance + /// Releases managed resources. /// - /// The - internal virtual ImageFrame ToFrame() + public void Dispose() { - return new ImageFrame(this); - } - - /// - protected override void Dispose(bool disposing) - { - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < this.Frames.Count; i++) - { - this.Frames[i].Dispose(); - } - - base.Dispose(disposing); + this.frames.Dispose(); } /// - /// Copies the properties from the other . + /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer. /// - /// - /// The other to copy the properties from. - /// - private void CopyProperties(IImage other) + /// The pixel source. + internal void SwapPixelsBuffers(Image pixelSource) { - this.MetaData = new ImageMetaData(other.MetaData); + Guard.NotNull(pixelSource, nameof(pixelSource)); + + for (int i = 0; i < this.frames.Count; i++) + { + this.frames[i].SwapPixelsBuffers(pixelSource.frames[i]); + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessorExtensions.cs b/src/ImageSharp/Image/PixelAccessorExtensions.cs new file mode 100644 index 0000000000..b628c05f8b --- /dev/null +++ b/src/ImageSharp/Image/PixelAccessorExtensions.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Helper methods fro acccess pixel accessors + /// + internal static class PixelAccessorExtensions + { + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The type of the pixel. + /// The frame. + /// + /// The + /// + internal static PixelAccessor Lock(this IPixelSource frame) + where TPixel : struct, IPixel + { + return new PixelAccessor(frame); + } + + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The type of the pixel. + /// The image. + /// + /// The + /// + internal static PixelAccessor Lock(this Image image) + where TPixel : struct, IPixel + { + return image.Frames.RootFrame.Lock(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 3902ba4255..8dcb1f7602 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Provides per-pixel access to generic pixels. /// @@ -25,6 +22,7 @@ namespace ImageSharp /// The containing the pixel data. /// internal Buffer2D PixelBuffer; + private bool ownedBuffer; #pragma warning restore SA1401 // Fields must be private /// @@ -42,14 +40,13 @@ namespace ImageSharp /// Initializes a new instance of the class. /// /// The image to provide pixel access for. - public PixelAccessor(ImageBase image) + public PixelAccessor(IPixelSource image) { Guard.NotNull(image, nameof(image)); - Guard.MustBeGreaterThan(image.Width, 0, "image width"); - Guard.MustBeGreaterThan(image.Height, 0, "image height"); + Guard.MustBeGreaterThan(image.PixelBuffer.Width, 0, "image width"); + Guard.MustBeGreaterThan(image.PixelBuffer.Height, 0, "image height"); - this.SetPixelBufferUnsafe(image.Width, image.Height, image.PixelBuffer); - this.ParallelOptions = image.Configuration.ParallelOptions; + this.SetPixelBufferUnsafe(image.PixelBuffer, false); } /// @@ -58,7 +55,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, Buffer2D.CreateClean(width, height)) + : this(width, height, Buffer2D.CreateClean(width, height), true) { } @@ -68,15 +65,14 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - private PixelAccessor(int width, int height, Buffer2D pixels) + /// if set to true [owned buffer]. + private PixelAccessor(int width, int height, Buffer2D pixels, bool ownedBuffer) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - this.SetPixelBufferUnsafe(width, height, pixels); - - this.ParallelOptions = Configuration.Default.ParallelOptions; + this.SetPixelBufferUnsafe(pixels, ownedBuffer); } /// @@ -102,21 +98,12 @@ namespace ImageSharp /// public int RowStride { get; private set; } - /// - /// Gets the width of the image. - /// + /// public int Width { get; private set; } - /// - /// Gets the height of the image. - /// + /// public int Height { get; private set; } - /// - /// Gets the global parallel options for processing tasks in parallel. - /// - public ParallelOptions ParallelOptions { get; } - /// Span IBuffer2D.Span => this.PixelBuffer; @@ -145,12 +132,10 @@ namespace ImageSharp } } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// public void Dispose() { - if (this.isDisposed) + if (this.isDisposed || !this.ownedBuffer) { return; } @@ -238,15 +223,13 @@ namespace ImageSharp /// /// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!! /// - /// The width. - /// The height. /// The pixels. /// Returns the old pixel data thats has gust been replaced. /// If is true then caller is responsible for ensuring is called. - internal TPixel[] ReturnCurrentColorsAndReplaceThemInternally(int width, int height, TPixel[] pixels) + internal Buffer2D SwapBufferOwnership(Buffer2D pixels) { - TPixel[] oldPixels = this.PixelBuffer.TakeArrayOwnership(); - this.SetPixelBufferUnsafe(width, height, pixels); + Buffer2D oldPixels = this.PixelBuffer; + this.SetPixelBufferUnsafe(pixels, this.ownedBuffer); return oldPixels; } @@ -414,23 +397,18 @@ namespace ImageSharp } } - private void SetPixelBufferUnsafe(int width, int height, TPixel[] pixels) - { - this.SetPixelBufferUnsafe(width, height, new Buffer2D(pixels, width, height)); - } - /// /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// - /// The width. - /// The height. /// The pixel buffer - private void SetPixelBufferUnsafe(int width, int height, Buffer2D pixels) + /// if set to true then this instance ownes the buffer and thus should dispose of it afterwards. + private void SetPixelBufferUnsafe(Buffer2D pixels, bool ownedBuffer) { this.PixelBuffer = pixels; + this.ownedBuffer = ownedBuffer; - this.Width = width; - this.Height = height; + this.Width = pixels.Width; + this.Height = pixels.Height; this.PixelSize = Unsafe.SizeOf(); this.RowStride = this.Width * this.PixelSize; } diff --git a/src/ImageSharp/Image/PixelArea{TPixel}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs index 4ddfcadb7b..1c7256455e 100644 --- a/src/ImageSharp/Image/PixelArea{TPixel}.cs +++ b/src/ImageSharp/Image/PixelArea{TPixel}.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.IO; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics; +using System.IO; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Represents an area of generic pixels. /// @@ -33,7 +30,7 @@ namespace ImageSharp /// /// The underlying buffer containing the raw pixel data. /// - private Buffer byteBuffer; + private readonly Buffer byteBuffer; /// /// Initializes a new instance of the class. @@ -119,7 +116,7 @@ namespace ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = new Buffer(this.Length); + this.byteBuffer = Buffer.CreateClean(this.Length); } /// diff --git a/src/ImageSharp/ImageFormats.cs b/src/ImageSharp/ImageFormats.cs index bc6e0f40f0..986c57c0bd 100644 --- a/src/ImageSharp/ImageFormats.cs +++ b/src/ImageSharp/ImageFormats.cs @@ -1,12 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +namespace SixLabors.ImageSharp +{ /// /// The static collection of all the default image formats /// @@ -30,7 +32,7 @@ namespace ImageSharp /// /// The format details for the bitmaps. /// - public static readonly IImageFormat Bitmap = new BmpFormat(); + public static readonly IImageFormat Bmp = new BmpFormat(); /// /// The format details for the tiffs. diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 8733131a74..9d30030da6 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -1,20 +1,21 @@  A cross-platform library for the processing of image files; written in C# - ImageSharp - 1.0.0-alpha9 - James Jackson-South and contributors - netstandard1.3;netstandard1.1 + SixLabors.ImageSharp + $(packageversion) + 0.0.1 + Six Labors and contributors + netstandard1.1;netstandard1.3;netstandard2.0 true true - ImageSharp - ImageSharp + SixLabors.ImageSharp + SixLabors.ImageSharp Image Resize Crop Gif Jpg Jpeg Bitmap Png Core - https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-128.png - https://github.com/JimBobSquarePants/ImageSharp + https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png + https://github.com/SixLabors/ImageSharp http://www.apache.org/licenses/LICENSE-2.0 git - https://github.com/JimBobSquarePants/ImageSharp + https://github.com/SixLabors/ImageSharp false false false @@ -31,28 +32,35 @@ - - - + + + All - - - - - + + + + + - + + + ..\..\ImageSharp.ruleset + SixLabors.ImageSharp true + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + TextTemplatingFileGenerator Block8x8F.Generated.cs @@ -78,6 +86,11 @@ + + True + True + Block8x8F.Generated.tt + True True diff --git a/src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject b/src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 046bfd81fc..ac5ab09dbd 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Memory +{ /// /// Defines extension methods for . /// @@ -41,5 +40,55 @@ namespace ImageSharp.Memory { return buffer.Span.Slice(y * buffer.Width, buffer.Width); } + + /// + /// Returns the size of the buffer. + /// + /// The element type + /// The + /// The of the buffer + public static Size Size(this IBuffer2D buffer) + where T : struct + { + return new Size(buffer.Width, buffer.Height); + } + + /// + /// Returns a representing the full area of the buffer. + /// + /// The element type + /// The + /// The + public static Rectangle FullRectangle(this IBuffer2D buffer) + where T : struct + { + return new Rectangle(0, 0, buffer.Width, buffer.Height); + } + + /// + /// Return a to the subarea represented by 'rectangle' + /// + /// The element type + /// The + /// The rectangle subarea + /// The + public static BufferArea GetArea(this IBuffer2D buffer, Rectangle rectangle) + where T : struct => new BufferArea(buffer, rectangle); + + public static BufferArea GetArea(this IBuffer2D buffer, int x, int y, int width, int height) + where T : struct + { + var rectangle = new Rectangle(x, y, width, height); + return new BufferArea(buffer, rectangle); + } + + /// + /// Return a to the whole area of 'buffer' + /// + /// The element type + /// The + /// The + public static BufferArea GetArea(this IBuffer2D buffer) + where T : struct => new BufferArea(buffer); } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer2D.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs similarity index 76% rename from src/ImageSharp/Memory/Buffer2D.cs rename to src/ImageSharp/Memory/Buffer2D{T}.cs index 59cabb1bd4..99b10cae7e 100644 --- a/src/ImageSharp/Memory/Buffer2D.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Memory +{ /// /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. @@ -15,6 +14,11 @@ namespace ImageSharp.Memory internal class Buffer2D : Buffer, IBuffer2D where T : struct { + public Buffer2D(Size size) + : this(size.Width, size.Height) + { + } + /// /// Initializes a new instance of the class. /// @@ -57,6 +61,9 @@ namespace ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] get { + DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); + DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); + return ref this.Array[(this.Width * y) + x]; } } @@ -73,5 +80,12 @@ namespace ImageSharp.Memory buffer.Clear(); return buffer; } + + /// + /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// + /// The size of the buffer + /// The instance + public static Buffer2D CreateClean(Size size) => CreateClean(size.Width, size.Height); } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs new file mode 100644 index 0000000000..b5ed3566fa --- /dev/null +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.CompilerServices; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Represents a rectangular area inside a 2D memory buffer (). + /// This type is kind-of 2D Span, but it can live on heap. + /// + /// The element type + internal struct BufferArea + where T : struct + { + /// + /// The rectangle specifying the boundaries of the area in . + /// + public readonly Rectangle Rectangle; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea(IBuffer2D destinationBuffer, Rectangle rectangle) + { + DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle)); + DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, destinationBuffer.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, destinationBuffer.Height, nameof(rectangle)); + + this.DestinationBuffer = destinationBuffer; + this.Rectangle = rectangle; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea(IBuffer2D destinationBuffer) + : this(destinationBuffer, destinationBuffer.FullRectangle()) + { + } + + /// + /// Gets the being pointed by this instance. + /// + public IBuffer2D DestinationBuffer { get; } + + /// + /// Gets the size of the area. + /// + public Size Size => this.Rectangle.Size; + + /// + /// Gets the pixel stride which is equal to the width of . + /// + public int Stride => this.DestinationBuffer.Width; + + /// + /// Gets or sets a value at the given index. + /// + /// The position inside a row + /// The row index + /// The reference to the value + public ref T this[int x, int y] => ref this.DestinationBuffer.Span[this.GetIndexOf(x, y)]; + + /// + /// Gets a reference to the [0,0] element. + /// + /// The reference to the [0,0] element + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T GetReferenceToOrigo() => + ref this.DestinationBuffer.Span[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; + + /// + /// Gets a span to row 'y' inside this area. + /// + /// The row index + /// The span + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int y) + { + int yy = this.GetRowIndex(y); + int xx = this.Rectangle.X; + int width = this.Rectangle.Width; + + return this.DestinationBuffer.Span.Slice(yy + xx, width); + } + + /// + /// Returns a sub-area as . (Similar to .) + /// + /// The x index at the subarea origo + /// The y index at the subarea origo + /// The desired width of the subarea + /// The desired height of the subarea + /// The subarea + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea GetSubArea(int x, int y, int width, int height) + { + var rectangle = new Rectangle(x, y, width, height); + return this.GetSubArea(rectangle); + } + + /// + /// Returns a sub-area as . (Similar to .) + /// + /// The specifying the boundaries of the subarea + /// The subarea + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea GetSubArea(Rectangle rectangle) + { + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle)); + + int x = this.Rectangle.X + rectangle.X; + int y = this.Rectangle.Y + rectangle.Y; + rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height); + return new BufferArea(this.DestinationBuffer, rectangle); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetIndexOf(int x, int y) + { + int yy = this.GetRowIndex(y); + int xx = this.Rectangle.X + x; + return yy + xx; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetRowIndex(int y) + { + return (y + this.Rectangle.Y) * this.DestinationBuffer.Width; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer.cs b/src/ImageSharp/Memory/Buffer{T}.cs similarity index 94% rename from src/ImageSharp/Memory/Buffer.cs rename to src/ImageSharp/Memory/Buffer{T}.cs index 4b3681c744..67af23426a 100644 --- a/src/ImageSharp/Memory/Buffer.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -1,20 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.Memory +{ + /// /// /// Manages a buffer of value type objects as a Disposable resource. /// The backing array is either pooled or comes from the outside. /// /// The value type. - internal class Buffer : IDisposable + internal class Buffer : IBuffer where T : struct { /// @@ -143,7 +142,7 @@ namespace ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Buffer CreateClean(int count) { - Buffer buffer = new Buffer(count); + var buffer = new Buffer(count); buffer.Clear(); return buffer; } @@ -207,7 +206,8 @@ namespace ImageSharp.Memory { if (this.IsDisposedOrLostArrayOwnership) { - throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); + throw new InvalidOperationException( + "TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); } this.IsDisposedOrLostArrayOwnership = true; diff --git a/src/ImageSharp/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs index 260c829e21..14ac58baf2 100644 --- a/src/ImageSharp/Memory/Fast2DArray{T}.cs +++ b/src/ImageSharp/Memory/Fast2DArray{T}.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Memory +{ /// /// Provides fast access to 2D arrays. /// @@ -106,11 +104,23 @@ namespace ImageSharp.Memory return new Fast2DArray(data); } + /// + /// Gets a representing the row beginning from the the first item on that row. + /// + /// The y-coordinate of the row. Must be greater than or equal to zero and less than the height of the 2D array. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int row) + { + this.CheckCoordinates(row); + return new Span(this.Data, row * this.Width, this.Width); + } + /// /// Checks the coordinates to ensure they are within bounds. /// - /// The row-coordinate of the item. Must be greater than zero and smaller than the height of the array. - /// The column-coordinate of the item. Must be greater than zero and smaller than the width of the array. + /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the array. + /// The x-coordinate of the item. Must be greater than zero and smaller than the width of the array. /// /// Thrown if the coordinates are not within the bounds of the array. /// @@ -127,5 +137,21 @@ namespace ImageSharp.Memory throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the array bounds."); } } + + /// + /// Checks the coordinates to ensure they are within bounds. + /// + /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the array. + /// + /// Thrown if the coordinates are not within the bounds of the image. + /// + [Conditional("DEBUG")] + private void CheckCoordinates(int row) + { + if (row < 0 || row >= this.Height) + { + throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds."); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/IBuffer2D.cs b/src/ImageSharp/Memory/IBuffer2D{T}.cs similarity index 79% rename from src/ImageSharp/Memory/IBuffer2D.cs rename to src/ImageSharp/Memory/IBuffer2D{T}.cs index 300c29a1ba..2f60fd02a0 100644 --- a/src/ImageSharp/Memory/IBuffer2D.cs +++ b/src/ImageSharp/Memory/IBuffer2D{T}.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; +using System; +namespace SixLabors.ImageSharp.Memory +{ /// /// An interface that represents a pinned buffer of value type objects /// interpreted as a 2D region of x elements. diff --git a/src/ImageSharp/Memory/IBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer{T}.cs new file mode 100644 index 0000000000..a0f80063f8 --- /dev/null +++ b/src/ImageSharp/Memory/IBuffer{T}.cs @@ -0,0 +1,18 @@ +using System; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// + /// Represents a contigous memory buffer of value-type items "promising" a + /// + /// The value type + internal interface IBuffer : IDisposable + where T : struct + { + /// + /// Gets the span to the memory "promised" by this buffer + /// + Span Span { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Memory/PixelDataPool{T}.cs index 643f1c6ca3..80c9c410e8 100644 --- a/src/ImageSharp/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Memory/PixelDataPool{T}.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System.Buffers; - - using ImageSharp.PixelFormats; +using System.Buffers; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Memory +{ /// /// Provides a resource pool that enables reusing instances of value type arrays for image data . /// @@ -17,9 +14,35 @@ namespace ImageSharp.Memory where T : struct { /// - /// The which is not kept clean. + /// The maximum size of pooled arrays in bytes. + /// Currently set to 32MB, which is equivalent to 8 megapixels of raw data. + /// + internal const int MaxPooledBufferSizeInBytes = 32 * 1024 * 1024; + + /// + /// The threshold to pool arrays in which has less buckets for memory safety. + /// + private const int LargeBufferThresholdInBytes = 8 * 1024 * 1024; + + /// + /// The maximum array length of the . + /// + private static readonly int MaxLargeArrayLength = MaxPooledBufferSizeInBytes / Unsafe.SizeOf(); + + /// + /// The maximum array length of the . + /// + private static readonly int MaxNormalArrayLength = LargeBufferThresholdInBytes / Unsafe.SizeOf(); + + /// + /// The for huge buffers, which is not kept clean. + /// + private static readonly ArrayPool LargeArrayPool = ArrayPool.Create(MaxLargeArrayLength, 8); + + /// + /// The for small-to-medium buffers which is not kept clean. /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); + private static readonly ArrayPool NormalArrayPool = ArrayPool.Create(MaxNormalArrayLength, 24); /// /// Rents the pixel array from the pool. @@ -28,7 +51,14 @@ namespace ImageSharp.Memory /// The public static T[] Rent(int minimumLength) { - return ArrayPool.Rent(minimumLength); + if (minimumLength <= MaxNormalArrayLength) + { + return NormalArrayPool.Rent(minimumLength); + } + else + { + return LargeArrayPool.Rent(minimumLength); + } } /// @@ -37,25 +67,13 @@ namespace ImageSharp.Memory /// The array to return to the buffer pool. public static void Return(T[] array) { - ArrayPool.Return(array); - } - - /// - /// Heuristically calculates a reasonable maxArrayLength value for the backing . - /// - /// The maxArrayLength value - internal static int CalculateMaxArrayLength() - { - // ReSharper disable once SuspiciousTypeConversion.Global - if (default(T) is IPixel) + if (array.Length <= MaxNormalArrayLength) { - const int MaximumExpectedImageSize = 16384 * 16384; - return MaximumExpectedImageSize; + NormalArrayPool.Return(array); } else { - const int MaxArrayLength = 1024 * 1024; // Match default pool. - return MaxArrayLength; + LargeArrayPool.Return(array); } } } diff --git a/src/ImageSharp/Memory/SpanHelper.cs b/src/ImageSharp/Memory/SpanHelper.cs index 0e794e1b57..73bc5f55d8 100644 --- a/src/ImageSharp/Memory/SpanHelper.cs +++ b/src/ImageSharp/Memory/SpanHelper.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Memory +{ /// /// Utility methods for /// diff --git a/src/ImageSharp/MetaData/IMetaData.cs b/src/ImageSharp/MetaData/IMetaData.cs deleted file mode 100644 index 6daa04dd63..0000000000 --- a/src/ImageSharp/MetaData/IMetaData.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using ImageSharp.Formats; - - /// - /// Encapsulates the metadata of an image frame. - /// - internal interface IMetaData - { - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, when utilized in Gif animation, this field specifies the number of hundredths (1/100) of a second to - /// wait before continuing with the processing of the Data Stream. - /// The clock starts ticking immediately after the graphic is rendered. - /// - int FrameDelay { get; set; } - - /// - /// Gets or sets the disposal method for animated images. - /// Primarily used in Gif animation, this field indicates the way in which the graphic is to - /// be treated after being displayed. - /// - DisposalMethod DisposalMethod { get; set; } - } -} diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs index b55bfd1ad1..ca3012f4aa 100644 --- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs +++ b/src/ImageSharp/MetaData/ImageFrameMetaData.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Encapsulates the metadata of an image frame. /// - public sealed class ImageFrameMetaData : IMetaData + public sealed class ImageFrameMetaData { /// /// Initializes a new instance of the class. @@ -34,10 +33,28 @@ namespace ImageSharp this.DisposalMethod = other.DisposalMethod; } - /// + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, when utilized in Gif animation, this field specifies the number of hundredths (1/100) of a second to + /// wait before continuing with the processing of the Data Stream. + /// The clock starts ticking immediately after the graphic is rendered. + /// public int FrameDelay { get; set; } - /// + /// + /// Gets or sets the disposal method for animated images. + /// Primarily used in Gif animation, this field indicates the way in which the graphic is to + /// be treated after being displayed. + /// public DisposalMethod DisposalMethod { get; set; } + + /// + /// Clones this ImageFrameMetaData. + /// + /// The cloned instance. + public ImageFrameMetaData Clone() + { + return new ImageFrameMetaData(this); + } } } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index 5361b486d4..e36f2a69f2 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Collections.Generic; - using ImageSharp.Formats; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Encapsulates the metadata of an image. /// - public sealed class ImageMetaData : IMetaData + public sealed class ImageMetaData { /// /// The default horizontal resolution value (dots per inch) in x direction. @@ -50,8 +51,6 @@ namespace ImageSharp this.HorizontalResolution = other.HorizontalResolution; this.VerticalResolution = other.VerticalResolution; - this.FrameDelay = other.FrameDelay; - this.DisposalMethod = other.DisposalMethod; this.RepeatCount = other.RepeatCount; foreach (ImageProperty property in other.Properties) @@ -114,12 +113,6 @@ namespace ImageSharp /// public IccProfile IccProfile { get; set; } - /// - public int FrameDelay { get; set; } - - /// - public DisposalMethod DisposalMethod { get; set; } - /// /// Gets the list of properties for storing meta information about this image. /// @@ -132,6 +125,15 @@ namespace ImageSharp /// public ushort RepeatCount { get; set; } + /// + /// Clones this into a new instance + /// + /// The cloned metadata instance + public ImageMetaData Clone() + { + return new ImageMetaData(this); + } + /// /// Synchronizes the profiles with the current meta data. /// diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index 178794283c..62ae9d4790 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/src/ImageSharp/MetaData/ImageProperty.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Stores meta information about a image, like the name of the author, /// the copyright information, the date, where the image was created diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs index f2d012c6c8..8c3c1171c7 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { /// /// Specifies exif data types. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs index 880a434534..b1b42ad433 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Specifies which parts will be written when the profile is added to an image. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index a7fd8fd6a8..f72753cc27 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents an EXIF profile providing access to the collection of values. /// @@ -82,9 +80,11 @@ namespace ImageSharp this.values.Add(new ExifValue(value)); } } - else + + if (other.data != null) { - this.data = other.data; + this.data = new byte[other.data.Length]; + Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length); } } @@ -131,7 +131,7 @@ namespace ImageSharp return null; } - if (this.data.Length < (this.thumbnailOffset + this.thumbnailLength)) + if (this.data == null || this.data.Length < (this.thumbnailOffset + this.thumbnailLength)) { return null; } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 53123bfc29..e247527a6e 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - using System.Text; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Reads and parses EXIF data from a byte array /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs index 6f4b494857..625f95b63d 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { /// /// All exif tags from the Exif standard 2.2 @@ -977,7 +975,7 @@ namespace ImageSharp [ExifTagDescription((ushort)71, "Fired, Red-eye reduction, Return detected")] [ExifTagDescription((ushort)73, "On, Red-eye reduction")] [ExifTagDescription((ushort)77, "On, Red-eye reduction, Return not detected")] - [ExifTagDescription((ushort)69, "On, Red-eye reduction, Return detected")] + [ExifTagDescription((ushort)79, "On, Red-eye reduction, Return detected")] [ExifTagDescription((ushort)80, "Off, Red-eye reduction")] [ExifTagDescription((ushort)88, "Auto, Did not fire, Red-eye reduction")] [ExifTagDescription((ushort)89, "Auto, Fired, Red-eye reduction")] diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs index e08bddd3ff..4021227f57 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Class that provides a description for an ExifTag value. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs index a2965917b1..64508137b4 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; - using System.Text; +using System; +using System.Globalization; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represent the value of the EXIF profile. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs index bf8f4caa26..7f4123225e 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Text; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Contains methods for writing EXIF metadata. /// diff --git a/src/ImageSharp/Numerics/LongRational.cs b/src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs similarity index 97% rename from src/ImageSharp/Numerics/LongRational.cs rename to src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs index f56abc2899..f9c16d57d4 100644 --- a/src/ImageSharp/Numerics/LongRational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; - using System.Text; +using System; +using System.Globalization; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents a number that can be expressed as a fraction /// diff --git a/src/ImageSharp/Numerics/Rational.cs b/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs similarity index 97% rename from src/ImageSharp/Numerics/Rational.cs rename to src/ImageSharp/MetaData/Profiles/Exif/Rational.cs index d219a54696..6d62a623f9 100644 --- a/src/ImageSharp/Numerics/Rational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents a number that can be expressed as a fraction. /// diff --git a/src/ImageSharp/Numerics/SignedRational.cs b/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs similarity index 97% rename from src/ImageSharp/Numerics/SignedRational.cs rename to src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs index bd2213dce0..f2fe359242 100644 --- a/src/ImageSharp/Numerics/SignedRational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents a number that can be expressed as a fraction. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs index f34f006e7c..157453f1b1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A segment of a curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs index ad03c8ff8a..5d931039c8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A formula based curve segment /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs index 3f6497cccf..d916486dbf 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A one dimensional curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs index 14aaf153ef..46aec49be6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A parametric curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs index a5816e212e..ae9cd84b47 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A response curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs index 859d43338e..572c4a8f7d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A sampled curve segment /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs index ba32586fea..0ea404ad9c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs index a34ee42fce..f9d56b0fff 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs index 23ad9feb22..495d73a784 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs index 371c5c42a6..2b39b1d6c6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs index 44a892084c..8b942498ae 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs index 85d80c7a84..14f7f95703 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Text; +using System; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index 711de818b3..5c14448fa5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; - using System.Numerics; +using System; +using System.Globalization; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// @@ -476,19 +474,7 @@ namespace ImageSharp string languageCode = this.ReadAsciiString(2); string countryCode = this.ReadAsciiString(2); - if (string.IsNullOrWhiteSpace(languageCode)) - { - culture[i] = CultureInfo.InvariantCulture; - } - else if (string.IsNullOrWhiteSpace(countryCode)) - { - culture[i] = new CultureInfo(languageCode); - } - else - { - culture[i] = new CultureInfo($"{languageCode}-{countryCode}"); - } - + culture[i] = ReadCulture(languageCode, countryCode); length[i] = this.ReadUInt32(); offset[i] = this.ReadUInt32(); } @@ -500,6 +486,36 @@ namespace ImageSharp } return new IccMultiLocalizedUnicodeTagDataEntry(text); + + CultureInfo ReadCulture(string language, string country) + { + if (string.IsNullOrWhiteSpace(language)) + { + return CultureInfo.InvariantCulture; + } + else if (string.IsNullOrWhiteSpace(country)) + { + try + { + return new CultureInfo(language); + } + catch (CultureNotFoundException) + { + return CultureInfo.InvariantCulture; + } + } + else + { + try + { + return new CultureInfo($"{language}-{country}"); + } + catch (CultureNotFoundException) + { + return ReadCulture(language, null); + } + } + } } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs index cb1fe5b55e..1fecd761a6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Text; - using ImageSharp.IO; +using System; +using System.Text; +using SixLabors.ImageSharp.IO; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs index 4b6e454a1d..d5f5d5a9b6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs index f85d59714f..38a3dd457d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs index 13a15b4836..0b7064cf93 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - - using ImageSharp.Memory; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs index b3ddb538cd..48c6734aec 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs index d90c1ff549..791a94a339 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs index fbb7065e6b..bf39a08491 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Text; +using System; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index b74f22054e..9ee4f00046 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Linq; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs index a9a65b80be..cfcc66c8e4 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - using System.Text; +using System; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs index 066cbe8489..fe0c9b8b59 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Color lookup table data type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs index 3f57ded742..0913cda418 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Color Space Type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs index 251a848b7b..bec71e0dbc 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Colorant Encoding diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs index 14515c113a..cfd2671e94 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Curve Measurement Encodings diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs index 77ded0d1b5..e9b8cc26b8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Curve Segment Signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs index a4ca4befab..b13edb53f4 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the basic data types as defined in ICC.1:2010 version 4.3.0.0 diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs index c57cf4f977..88cca866dc 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Device attributes. Can be combined with a logical OR /// The least-significant 32 bits are defined by the ICC, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs index eacc1eb281..2a375b0d1c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Formula curve segment type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs index fdfb78049d..fe6575c41b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Measurement Geometry diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs index 8ab690b64f..fe5d309228 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Multi process element signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs index 823b413402..374b4ad93b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Formula curve segment type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs index 8fdeb3f418..1563078f72 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the primary platform/operating system framework for which the profile was created diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs index 9fb0b51f32..71c27ca611 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Profile Class Name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs index aa8df88bd8..3758be34b7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Profile flags. Can be combined with a logical OR. /// The least-significant 16 bits are reserved for the ICC, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs index 6eab5b3fe8..82b2969003 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs @@ -1,10 +1,8 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// // ReSharper disable InconsistentNaming -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the ICC Profile Tags as defined in ICC.1:2010 version 4.3.0.0 diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs index 10e0fbac99..e0504b24cb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Rendering intent diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs index 2938d44698..839ab8c6b1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Screening flags. Can be combined with a logical OR. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs index 0d24c3ae5b..f1d73c6268 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the screening spot types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs index 5fc2fa228c..4773ab6c2a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Signature Name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs index 3526887edc..fe0c406576 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Standard Illuminant diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs index 0efc5fd406..a393c258b2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Standard Observer diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs index a42cc8cd11..1493ecc6bb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Type Signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs b/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs index 54fe7c7641..d867edbebb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Represents an error that happened while reading or writing a corrupt/invalid ICC profile /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs index 978d5bc24f..df85b2ab8e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; + #if !NETSTANDARD1_1 - using System.Security.Cryptography; +using System.Security.Cryptography; #endif +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Represents an ICC profile /// @@ -59,7 +58,11 @@ namespace ImageSharp Guard.NotNull(other, nameof(other)); // TODO: Do we need to copy anything else? - this.data = other.data; + if (other.data != null) + { + this.data = new byte[other.data.Length]; + Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length); + } } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs index 8237dc7a82..f91572cfe6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Contains all values of an ICC profile header /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs index b24c96f023..ea84113fd9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Reads and parses ICC data from a byte array /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs index 5db3d96eac..4789a69fe7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The data of an ICC tag entry /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index 19c00e8f55..d7221e8cf8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Contains methods for writing ICC profiles. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs index a20a52d8e6..09931a1f91 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A placeholder (might be used for future ICC versions) /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs index 7d5855168e..384ae850f3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A CLUT (color lookup table) element to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs index c16ca93d24..d04869548b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A set of curves to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs index 9219f52002..7e0e1eda82 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A placeholder (might be used for future ICC versions) /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs index 259f714896..642f766d56 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - - using ImageSharp.Memory; +using System; +using System.Linq; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A matrix element to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs index 205e61f011..59acd0eb7f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// An element to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs index 85f40f5e41..6d838558a2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The chromaticity tag type provides basic chromaticity data /// and type of phosphors or colorants of a monitor to applications and utilities. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs index 9be5541a28..56af95a169 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This tag specifies the laydown order in which colorants /// will be printed on an n-colorant device. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs index 041f74f391..b04ee10d00 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The purpose of this tag is to identify the colorants used in /// the profile by a unique name and set of PCSXYZ or PCSLAB values diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs index 73588bbf05..1e516ce7f0 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type contains the PostScript product name to which this profile /// corresponds and the names of the companion CRDs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs index c14995748b..7f753ff7d5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The type contains a one-dimensional table of double values. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs index 57f04b11ae..3b17e29429 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Text; +using System; +using System.Linq; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The dataType is a simple data structure that contains /// either 7-bit ASCII or binary data, i.e. textType data or transparent bytes. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs index 7111913cbf..879c208c1e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type is a representation of the time and date. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs index c91347d23f..23580fac6c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of doubles (from 32bit fixed point values). /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs index 45d7b3c50c..13e1463309 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform using tables /// with 16-bit precision. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs index 538cf0505b..fd6d028eaa 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform using tables /// with 8-bit precision. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs index d440447cca..faf9a45506 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs index 9d63dd171f..10ffca3356 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs index 390a5ba54d..12010ea09c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The measurementType information refers only to the internal /// profile data and is meant to provide profile makers an alternative diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs index 573e77ed47..62792b44ee 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This tag structure contains a set of records each referencing /// a multilingual string associated with a profile. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs index e3e6ef1437..437c6734bb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform, containing /// a sequence of processing elements. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs index 17b7213b24..d3e73b0189 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The namedColor2Type is a count value and array of structures /// that provide color coordinates for color names. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs index 4264c0b24d..192be52a13 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The parametricCurveType describes a one-dimensional curve by /// specifying one of a predefined set of functions using the parameters. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs index 547483e233..88aaa09768 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type is an array of structures, each of which contains information /// from the header fields and tags from the original profiles which were diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs index 1a1b02c0bc..7ef17d37ec 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type is an array of structures, each of which contains information /// for identification of a profile used in a sequence. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs index f4fc7fbd21..7faad30f32 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The purpose of this tag type is to provide a mechanism to relate physical /// colorant amounts with the normalized device codes produced by lut8Type, lut16Type, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs index 8c3a2d83b1..7a5062707d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type describes various screening parameters including /// screen frequency, screening angle, and spot shape. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs index 9ce2c1b9ae..82462afa3c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Typically this type is used for registered tags that can /// be displayed on many development systems as a sequence of four characters. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs index 202220f93f..71030c2a62 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The TextDescriptionType contains three types of text description. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs index 9e56a1cf8a..8127fae29e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This is a simple text structure that contains a text string. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs index 2b7b4044d8..709106a52e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of doubles (from 32bit values). /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs index da77253102..bcd991ceae 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of unsigned shorts. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs index 0dabdecc12..8a5ccb0c08 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of unsigned 32bit integers. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs index d62a4a8a79..6e22f77585 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of unsigned 64bit integers. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs index 30363df9dd..07e142d49e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of bytes. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs index e668e5cfdd..0f8bd6c33d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type contains curves representing the under color removal and black generation /// and a text string which is a general description of the method used for the UCR and BG. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs index 79549b7c81..8b60aad266 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This tag stores data of an unknown tag data entry /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs index 5f7406ef11..2c12066f1c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents a set of viewing condition parameters. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs index 77f9ff7069..d704fee969 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The XYZType contains an array of XYZ values. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs index d527d4052e..af7e3e0114 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Color Lookup Table /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs index bd89550750..17127110d6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Entry of ICC colorant table /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs index 0e27729638..68a5e7cfa1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A string with a specific locale /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs index 9b86e11139..12d3208a7b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Lookup Table /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs index 5d51615684..98107e8281 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A specific color with a name /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs index 768aead0ef..6258ca2f36 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Position of an object within an ICC profile /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs index 4f744631d8..455717a030 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// ICC Profile description /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs index fc4760d3f1..1f96540df3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// ICC Profile ID /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs index 65507bf6bf..dfa3fb2037 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Description of a profile within a sequence /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs index 5c58aa1b16..a10c55f4e4 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Associates a normalized device code with a measurement value /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs index ac0e89bb05..f41858f303 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A single channel of a /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs index 7937569991..5464de9c5f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Entry of ICC tag table /// diff --git a/src/ImageSharp/Numerics/ValueSize.cs b/src/ImageSharp/Numerics/ValueSize.cs new file mode 100644 index 0000000000..659e0ebfe1 --- /dev/null +++ b/src/ImageSharp/Numerics/ValueSize.cs @@ -0,0 +1,129 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents a value in relation to a value on the image + /// + internal struct ValueSize : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// The value. + /// The type. + public ValueSize(float value, ValueSizeType type) + { + if (type != ValueSizeType.Absolute) + { + Guard.MustBeBetweenOrEqualTo(value, 0, 1, nameof(value)); + } + + this.Value = value; + this.Type = type; + } + + /// + /// The different vlaue types + /// + public enum ValueSizeType + { + /// + /// The value is the final return value + /// + Absolute, + + /// + /// The value is a percentage of the Images Width + /// + PercentageOfWidth, + + /// + /// The value is a percentage of the Images height + /// + PercentageOfHeight + } + + /// + /// Gets the value. + /// + public float Value { get; } + + /// + /// Gets the type. + /// + public ValueSizeType Type { get; } + + /// + /// Implicitly converts a float into an absolute value + /// + /// the vlaue to use as the absolute figure. + public static implicit operator ValueSize(float d) + => Absolute(d); + + /// + /// Create a new ValueSize with as a PercentageOfWidth type with value set to percentage. + /// + /// The percentage. + /// a Values size with type PercentageOfWidth + public static ValueSize PercentageOfWidth(float percentage) + { + return new ValueSize(percentage, ValueSizeType.PercentageOfWidth); + } + + /// + /// Create a new ValueSize with as a PercentageOfHeight type with value set to percentage. + /// + /// The percentage. + /// a Values size with type PercentageOfHeight + public static ValueSize PercentageOfHeight(float percentage) + { + return new ValueSize(percentage, ValueSizeType.PercentageOfHeight); + } + + /// + /// Create a new ValueSize with as a Absolute type with value set to value. + /// + /// The value. + /// a Values size with type Absolute( + public static ValueSize Absolute(float value) + { + return new ValueSize(value, ValueSizeType.Absolute); + } + + /// + /// Calculates the specified size. + /// + /// The size. + /// The calucalted value + public float Calculate(Size size) + { + switch (this.Type) + { + case ValueSizeType.PercentageOfWidth: + return this.Value * size.Width; + case ValueSizeType.PercentageOfHeight: + return this.Value * size.Height; + case ValueSizeType.Absolute: + default: + return this.Value; + } + } + + /// + public override string ToString() + { + return $"{this.Value} - {this.Type}"; + } + + /// + public bool Equals(ValueSize other) + { + return this.Type == other.Type && this.Value == other.Value; + } + } +} diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs index 59934fdc64..c266035a6b 100644 --- a/src/ImageSharp/PixelFormats/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/Alpha8.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing a single 8 bit normalized W values. /// diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/Argb32.cs index f389723dc3..33294838e6 100644 --- a/src/ImageSharp/PixelFormats/Argb32.cs +++ b/src/ImageSharp/PixelFormats/Argb32.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in alpha, red, green, and blue order. diff --git a/src/ImageSharp/PixelFormats/Bgr24.cs b/src/ImageSharp/PixelFormats/Bgr24.cs index aaed5c3852..e210856b36 100644 --- a/src/ImageSharp/PixelFormats/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/Bgr24.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in blue, green, red order. diff --git a/src/ImageSharp/PixelFormats/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs index af22b14a09..b4e7aad583 100644 --- a/src/ImageSharp/PixelFormats/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/Bgr565.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. /// diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/Bgra32.cs index f1ac20b567..e8469414d3 100644 --- a/src/ImageSharp/PixelFormats/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/Bgra32.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in blue, green, red, and alpha order. diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs index 746e1062ba..c51a872d1a 100644 --- a/src/ImageSharp/PixelFormats/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/Bgra4444.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. /// diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs index 198f911088..8be4ce82c9 100644 --- a/src/ImageSharp/PixelFormats/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/Bgra5551.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. /// diff --git a/src/ImageSharp/PixelFormats/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs index 14053ba122..829937c8ad 100644 --- a/src/ImageSharp/PixelFormats/Byte4.cs +++ b/src/ImageSharp/PixelFormats/Byte4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. /// diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index 92fb006abf..c646d804ac 100644 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// A set of named colors mapped to the provided Color space. /// diff --git a/src/ImageSharp/PixelFormats/ColorConstants.cs b/src/ImageSharp/PixelFormats/ColorConstants.cs index 236d3a889e..f97d3b190e 100644 --- a/src/ImageSharp/PixelFormats/ColorConstants.cs +++ b/src/ImageSharp/PixelFormats/ColorConstants.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Provides useful color definitions. /// diff --git a/src/ImageSharp/PixelFormats/ComponentOrder.cs b/src/ImageSharp/PixelFormats/ComponentOrder.cs index 8f151b41d6..868d082599 100644 --- a/src/ImageSharp/PixelFormats/ComponentOrder.cs +++ b/src/ImageSharp/PixelFormats/ComponentOrder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +namespace SixLabors.ImageSharp.PixelFormats { /// /// Enumerates the various component orders. /// - public enum ComponentOrder + internal enum ComponentOrder { /// /// Z-> Y-> X order. Equivalent to B-> G-> R in diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs index c042d76786..9553ec82d6 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -1,14 +1,10 @@ -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +// +namespace SixLabors.ImageSharp.PixelFormats { using System; - using System.Numerics; using System.Runtime.CompilerServices; public partial class PixelOperations diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt index 16292489fc..aa88b6606c 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -95,17 +93,13 @@ } #> -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +// +namespace SixLabors.ImageSharp.PixelFormats { using System; - using System.Numerics; using System.Runtime.CompilerServices; public partial class PixelOperations diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs index e42c575d89..659e702281 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -1,19 +1,13 @@ -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +// +namespace SixLabors.ImageSharp { using System; - using System.Numerics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; /// /// Provides optimized overrides for bulk operations. @@ -38,7 +32,6 @@ namespace ImageSharp Unsafe.As(ref dp) = sp; dp.A = 255; } } - /// internal override void ToRgb24(Span sourcePixels, Span dest, int count) @@ -55,7 +48,6 @@ namespace ImageSharp dp = Unsafe.As(ref sp); } } - /// internal override void PackFromBgr24(Span source, Span destPixels, int count) @@ -72,7 +64,6 @@ namespace ImageSharp dp.Bgr = sp; dp.A = 255; } } - /// internal override void ToBgr24(Span sourcePixels, Span dest, int count) @@ -89,7 +80,6 @@ namespace ImageSharp dp = sp.Bgr; } } - /// internal override void PackFromBgra32(Span source, Span destPixels, int count) @@ -106,7 +96,6 @@ namespace ImageSharp dp = sp.ToRgba32(); } } - /// internal override void ToBgra32(Span sourcePixels, Span dest, int count) @@ -123,7 +112,6 @@ namespace ImageSharp dp = sp.ToBgra32(); } } - } } diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt index 9c01fa9157..9d22293947 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -11,7 +9,6 @@ <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> <# - void GeneratePackFromMethod(string pixelType, string converterCode) { #> @@ -31,7 +28,6 @@ <#=converterCode#> } } - <# } @@ -54,27 +50,19 @@ <#=converterCode#> } } - <# } - #> -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +// +namespace SixLabors.ImageSharp { using System; - using System.Numerics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; /// /// Provides optimized overrides for bulk operations. diff --git a/src/ImageSharp/PixelFormats/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs index 3bdfc9f1cf..b4bc491ebd 100644 --- a/src/ImageSharp/PixelFormats/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/HalfSingle.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing a single 16 bit floating point value. /// @@ -180,7 +178,7 @@ namespace ImageSharp.PixelFormats [MethodImpl(MethodImplOptions.AggressiveInlining)] private Vector4 ToScaledVector4() { - Vector4 vector = this.ToVector4(); + var vector = this.ToVector4(); vector *= MaxBytes; vector += Half; vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); diff --git a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs index 740795adc6..4d6ef0fb40 100644 --- a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs +++ b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Helper methods for packing and unpacking floating point values /// diff --git a/src/ImageSharp/PixelFormats/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs index 7f1fe4ebdd..aa5f321908 100644 --- a/src/ImageSharp/PixelFormats/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/HalfVector2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit floating-point values. /// diff --git a/src/ImageSharp/PixelFormats/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs index 062287dbe6..87a1c9a498 100644 --- a/src/ImageSharp/PixelFormats/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/HalfVector4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit floating-point values. /// diff --git a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs index ec283e6f2a..6775cbc589 100644 --- a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs +++ b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; +using System; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// This interface exists for ensuring signature compatibility to MonoGame and XNA packed color types. /// diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 9090e1210c..37c505c848 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// An interface that represents a generic pixel type. /// diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index 0b55dcbf91..45050de72e 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +namespace SixLabors.ImageSharp.PixelFormats { /// /// A set of named colors mapped to the provided color space. diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs index 992986f92b..9a69f6ab36 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs index 99f603f690..920f92cae7 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/NormalizedShort2.cs index a0615563f0..6d28f61c25 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs index f35fb63684..45f984da0b 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs index 29ef5675e7..ae5f785a96 100644 --- a/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs +++ b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Assists with the conversion of known packed pixel formats from one to another. /// @@ -21,18 +19,11 @@ namespace ImageSharp.PixelFormats /// /// Returns the correct scaling function for the given types The compute scale function. /// - /// The scale function. /// The source pixel format. /// The target pixel format. /// The - public static Func ComputeScaleFunction(Func scaleFunc) + public static Func ComputeScaleFunction() { - // Custom type with a custom function. - if (scaleFunc != null) - { - return scaleFunc; - } - Type source = typeof(TPixel); Type target = typeof(TPixel2); @@ -299,9 +290,11 @@ namespace ImageSharp.PixelFormats /// The private static bool IsStandardNormalizedType(Type type) { - return type == typeof(Rgba32) + return + type == typeof(Alpha8) || type == typeof(Argb32) - || type == typeof(Alpha8) + || type == typeof(Bgr24) + || type == typeof(Bgra32) || type == typeof(Bgr565) || type == typeof(Bgra4444) || type == typeof(Bgra5551) @@ -309,8 +302,10 @@ namespace ImageSharp.PixelFormats || type == typeof(HalfVector2) || type == typeof(HalfVector4) || type == typeof(Rg32) - || type == typeof(Rgba1010102) - || type == typeof(Rgba64); + || type == typeof(Rgb24) + || type == typeof(Rgba32) + || type == typeof(Rgba64) + || type == typeof(Rgba1010102); } /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index 1e48f71814..ae70cdee43 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Collections.Generic; - using System.Text; +using System; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// The various blending modes. /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 915d9a9244..99a20516d2 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -1,14 +1,12 @@ -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders +// +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { using System; using System.Numerics; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 230b05499f..9d7d73db99 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -10,17 +8,15 @@ <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders +// +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { using System; using System.Numerics; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 4213be0bae..21ae335bee 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -1,15 +1,14 @@ -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System.Numerics; - using System.Runtime.CompilerServices; +// +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ internal static partial class PorterDuffFunctions { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 53d22d8f38..c92ab6dd62 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -10,18 +8,17 @@ <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System.Numerics; - using System.Runtime.CompilerServices; +// +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ internal static partial class PorterDuffFunctions { <# diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index b1fca9520c..f09d6d51ca 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ /// /// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model. /// diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 1a1d1cd054..54cb09c28a 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; +using System; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Abstract base class for calling pixel composition functions /// diff --git a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs b/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs deleted file mode 100644 index 1ea2628951..0000000000 --- a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace ImageSharp.PixelFormats -{ - using System; - using System.Runtime.CompilerServices; - - /// - /// Extension methods for copying single pixel data into byte Spans. - /// TODO: This utility class exists for legacy reasons. Need to do a lot of chore work to remove it (mostly in test classes). - /// - internal static class PixelConversionExtensions - { - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToXyzBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Rgb24 dest = ref bytes.GetRgb24(startIndex); - pixel.ToRgb24(ref dest); - } - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToXyzwBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Rgba32 dest = ref Unsafe.As(ref bytes[startIndex]); - pixel.ToRgba32(ref dest); - } - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToZyxBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Bgr24 dest = ref Unsafe.As(ref bytes[startIndex]); - pixel.ToBgr24(ref dest); - } - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in - /// - /// The pixel type. - /// The pixel to copy the data from. - /// The bytes to set the color in. - /// The starting index of the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToZyxwBytes(this TPixel pixel, Span bytes, int startIndex) - where TPixel : struct, IPixel - { - ref Bgra32 dest = ref Unsafe.As(ref bytes[startIndex]); - pixel.ToBgra32(ref dest); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index 5a3737dc66..154ec73738 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Provides access to pixel blenders /// diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index a62d14527a..4f879fbdc7 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// for pixel buffers of type . diff --git a/src/ImageSharp/PixelFormats/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs index 0575689a77..d2c296515f 100644 --- a/src/ImageSharp/PixelFormats/Rg32.cs +++ b/src/ImageSharp/PixelFormats/Rg32.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. /// diff --git a/src/ImageSharp/PixelFormats/Rgb24.cs b/src/ImageSharp/PixelFormats/Rgb24.cs index b8cc8dc24f..d867d9065e 100644 --- a/src/ImageSharp/PixelFormats/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/Rgb24.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in red, green, blue order. @@ -128,5 +126,11 @@ namespace ImageSharp.PixelFormats dest.B = this.B; dest.A = 255; } + + /// + public override string ToString() + { + return $"({this.R},{this.G},{this.B})"; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs index e682aa4772..e6967d23ea 100644 --- a/src/ImageSharp/PixelFormats/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/Rgba1010102.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed vector type containing unsigned normalized values ranging from 0 to 1. /// The x, y and z components use 10 bits, and the w component uses 2 bits. diff --git a/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs b/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs index ab4c2ea606..b5fed5a357 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Provides standardized deifinitions for named colors. /// diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index 63e40e9cf7..552ac0a018 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Provides optimized overrides for bulk operations. /// @@ -53,10 +50,10 @@ namespace ImageSharp nameof(count), "Argument 'count' should divisible by Vector.Count!"); - Vector bVec = new Vector(256.0f / 255.0f); - Vector magicFloat = new Vector(32768.0f); - Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f - Vector mask = new Vector(255); + var bVec = new Vector(256.0f / 255.0f); + var magicFloat = new Vector(32768.0f); + var magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f + var mask = new Vector(255); int unpackedRawCount = count * 4; @@ -83,7 +80,7 @@ namespace ImageSharp vi &= mask; vi |= magicInt; - Vector vf = Vector.AsVectorSingle(vi); + var vf = Vector.AsVectorSingle(vi); vf = (vf - magicFloat) * bVec; Unsafe.Add(ref destBaseAsFloat, i) = vf; @@ -104,7 +101,6 @@ namespace ImageSharp } int remainder = count % Vector.Count; - int alignedCount = count - remainder; if (alignedCount > 0) @@ -120,6 +116,35 @@ namespace ImageSharp } } + internal override void PackFromVector4(Span sourceVectors, Span destColors, int count) + { + GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count); + + if (!SimdUtils.IsAvx2CompatibleArchitecture) + { + base.PackFromVector4(sourceVectors, destColors, count); + return; + } + + int remainder = count % 2; + int alignedCount = count - remainder; + + if (alignedCount > 0) + { + Span flatSrc = sourceVectors.Slice(0, alignedCount).NonPortableCast(); + Span flatDest = destColors.NonPortableCast(); + + SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(flatSrc, flatDest); + } + + if (remainder > 0) + { + // actually: remainder == 1 + int lastIdx = count - 1; + destColors[lastIdx].PackFromVector4(sourceVectors[lastIdx]); + } + } + /// internal override void PackFromRgba32(Span source, Span destPixels, int count) { diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs index 85322c7c5c..83a35f1895 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in red, green, blue, and alpha order. @@ -367,7 +364,7 @@ namespace ImageSharp /// A string representation of the packed vector. public override string ToString() { - return this.ToVector4().ToString(); + return $"({this.R},{this.G},{this.B},{this.A})"; } /// @@ -432,7 +429,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(float x, float y, float z, float w) { - Vector4 value = new Vector4(x, y, z, w); + var value = new Vector4(x, y, z, w); this.Pack(ref value); } @@ -443,7 +440,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector3 vector) { - Vector4 value = new Vector4(vector, 1); + var value = new Vector4(vector, 1); this.Pack(ref value); } diff --git a/src/ImageSharp/PixelFormats/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs index bdcf13763d..040ae15b1c 100644 --- a/src/ImageSharp/PixelFormats/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/Rgba64.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1. /// diff --git a/src/ImageSharp/PixelFormats/RgbaComponent.cs b/src/ImageSharp/PixelFormats/RgbaComponent.cs index ed85fb86bb..76df62c437 100644 --- a/src/ImageSharp/PixelFormats/RgbaComponent.cs +++ b/src/ImageSharp/PixelFormats/RgbaComponent.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Enumerates the RGBA (red, green, blue, alpha) color components. /// - public enum RgbaComponent + internal enum RgbaComponent { /// /// The red component. diff --git a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs index dc965f9ff5..2ef37c43ae 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +namespace SixLabors.ImageSharp.PixelFormats { /// /// Provides operators and composition algorithms. diff --git a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs index 00b7461661..1886df29f1 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Provides optimized overrides for bulk operations. /// diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs index c6eed1122e..ba641d590c 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. /// The color components are stored in red, green, blue, and alpha order. diff --git a/src/ImageSharp/PixelFormats/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs index 0b3f4be934..1355a94135 100644 --- a/src/ImageSharp/PixelFormats/Short2.cs +++ b/src/ImageSharp/PixelFormats/Short2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit signed integer values. /// diff --git a/src/ImageSharp/PixelFormats/Short4.cs b/src/ImageSharp/PixelFormats/Short4.cs index 9583009293..aecb4d2de8 100644 --- a/src/ImageSharp/PixelFormats/Short4.cs +++ b/src/ImageSharp/PixelFormats/Short4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit signed integer values. /// diff --git a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs index e37f80c25a..5a165659b8 100644 --- a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs +++ b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,11 @@ namespace ImageSharp /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The . - public static Image BinaryThreshold(this Image source, float threshold) + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) where TPixel : struct, IPixel { - return BinaryThreshold(source, threshold, source.Bounds); + source.ApplyProcessor(new BinaryThresholdProcessor(threshold)); + return source; } /// @@ -40,7 +37,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle) + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); diff --git a/src/ImageSharp/Processing/Binarization/Dither.cs b/src/ImageSharp/Processing/Binarization/Dither.cs index efaf63b06c..f21ccf0bd3 100644 --- a/src/ImageSharp/Processing/Binarization/Dither.cs +++ b/src/ImageSharp/Processing/Binarization/Dither.cs @@ -1,22 +1,33 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; - using ImageSharp.Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// The . + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new OrderedDitherProcessor(dither, 0)); + return source; + } + /// /// Dithers the image reducing it to two colors using ordered dithering. /// @@ -25,10 +36,28 @@ namespace ImageSharp /// The ordered ditherer. /// The component index to test the threshold against. Must range from 0 to 3. /// The . - public static Image Dither(this Image source, IOrderedDither dither, int index = 0) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, int index) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new OrderedDitherProcessor(dither, index)); + return source; + } + + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel { - return Dither(source, dither, source.Bounds, index); + source.ApplyProcessor(new OrderedDitherProcessor(dither, 0), rectangle); + return source; } /// @@ -42,7 +71,7 @@ namespace ImageSharp /// /// The component index to test the threshold against. Must range from 0 to 3. /// The . - public static Image Dither(this Image source, IOrderedDither dither, Rectangle rectangle, int index = 0) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle, int index) where TPixel : struct, IPixel { source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); @@ -57,10 +86,11 @@ namespace ImageSharp /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The . - public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) where TPixel : struct, IPixel { - return Dither(source, diffuser, threshold, source.Bounds); + source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold)); + return source; } /// @@ -74,7 +104,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs index 6a4e3807ba..d64db34bad 100644 --- a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs +++ b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs @@ -1,18 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +18,11 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image BlackWhite(this Image source) + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) where TPixel : struct, IPixel { - return BlackWhite(source, source.Bounds); + source.ApplyProcessor(new BlackWhiteProcessor()); + return source; } /// @@ -39,11 +34,11 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BlackWhite(this Image source, Rectangle rectangle) + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); return source; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ColorMatrix/Brightness.cs b/src/ImageSharp/Processing/ColorMatrix/Brightness.cs new file mode 100644 index 0000000000..34b5347841 --- /dev/null +++ b/src/ImageSharp/Processing/ColorMatrix/Brightness.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the brightness component of the image. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The . + public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BrightnessProcessor(amount)); + + /// + /// Alters the brightness component of the image. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); + } +} diff --git a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs index 14641afba1..ebfa9ffdcd 100644 --- a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs +++ b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,10 +21,11 @@ namespace ImageSharp /// The image this method extends. /// The type of color blindness simulator to apply. /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness) + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindness colorBlindness) where TPixel : struct, IPixel { - return ColorBlindness(source, colorBlindness, source.Bounds); + source.ApplyProcessor(GetProcessor(colorBlindness)); + return source; } /// @@ -41,48 +38,35 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle) + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindness colorBlindness, Rectangle rectangle) where TPixel : struct, IPixel { - IImageProcessor processor; + source.ApplyProcessor(GetProcessor(colorBlindness), rectangle); + return source; + } + private static IImageProcessor GetProcessor(ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { switch (colorBlindness) { case ImageSharp.Processing.ColorBlindness.Achromatomaly: - processor = new AchromatomalyProcessor(); - break; - + return new AchromatomalyProcessor(); case ImageSharp.Processing.ColorBlindness.Achromatopsia: - processor = new AchromatopsiaProcessor(); - break; - + return new AchromatopsiaProcessor(); case ImageSharp.Processing.ColorBlindness.Deuteranomaly: - processor = new DeuteranomalyProcessor(); - break; - + return new DeuteranomalyProcessor(); case ImageSharp.Processing.ColorBlindness.Deuteranopia: - processor = new DeuteranopiaProcessor(); - break; - + return new DeuteranopiaProcessor(); case ImageSharp.Processing.ColorBlindness.Protanomaly: - processor = new ProtanomalyProcessor(); - break; - + return new ProtanomalyProcessor(); case ImageSharp.Processing.ColorBlindness.Protanopia: - processor = new ProtanopiaProcessor(); - break; - + return new ProtanopiaProcessor(); case ImageSharp.Processing.ColorBlindness.Tritanomaly: - processor = new TritanomalyProcessor(); - break; - + return new TritanomalyProcessor(); default: - processor = new TritanopiaProcessor(); - break; + return new TritanopiaProcessor(); } - - source.ApplyProcessor(processor, rectangle); - return source; } } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Contrast.cs b/src/ImageSharp/Processing/ColorMatrix/Contrast.cs new file mode 100644 index 0000000000..e0f388e287 --- /dev/null +++ b/src/ImageSharp/Processing/ColorMatrix/Contrast.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the contrast component of the image. + /// + /// + /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The . + public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) + where TPixel : struct, IPixel + => source.ApplyProcessor(new ContrastProcessor(amount)); + + /// + /// Alters the contrast component of the image. + /// + /// + /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Effects/Contrast.cs b/src/ImageSharp/Processing/ColorMatrix/Filter.cs similarity index 52% rename from src/ImageSharp/Processing/Effects/Contrast.cs rename to src/ImageSharp/Processing/ColorMatrix/Filter.cs index 0a55fd067b..7eb684978a 100644 --- a/src/ImageSharp/Processing/Effects/Contrast.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Filter.cs @@ -1,50 +1,47 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { /// - /// Alters the contrast component of the image. + /// Filters an image but the given color matrix /// /// The pixel format. /// The image this method extends. - /// The new contrast of the image. Must be between -100 and 100. + /// The filter color matrix /// The . - public static Image Contrast(this Image source, int amount) + public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix) where TPixel : struct, IPixel { - return Contrast(source, amount, source.Bounds); + source.ApplyProcessor(new FilterProcessor(matrix)); + return source; } /// - /// Alters the contrast component of the image. + /// Filters an image but the given color matrix /// /// The pixel format. /// The image this method extends. - /// The new contrast of the image. Must be between -100 and 100. + /// The filter color matrix /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Contrast(this Image source, int amount, Rectangle rectangle) + public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix, Rectangle rectangle) where TPixel : struct, IPixel { - source.ApplyProcessor(new ContrastProcessor(amount), rectangle); + source.ApplyProcessor(new FilterProcessor(matrix), rectangle); return source; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs index 635b6747a6..ee43d5b016 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -22,23 +19,49 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Grayscale(this Image source) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source) where TPixel : struct, IPixel - { - return Grayscale(source, GrayscaleMode.Bt709); - } + => Grayscale(source, GrayscaleMode.Bt709); /// - /// Applies Grayscale toning to the image. + /// Applies Grayscale toning to the image using the given amount. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be between 0 and 1. + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) + where TPixel : struct, IPixel + => Grayscale(source, GrayscaleMode.Bt709, amount); + + /// + /// Applies grayscale toning to the image with the given . + /// + /// The pixel format. + /// The image this method extends. + /// The formula to apply to perform the operation. + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) + where TPixel : struct, IPixel + => Grayscale(source, mode, 1F); + + /// + /// Applies grayscale toning to the image with the given using the given amount. /// /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. + /// The proportion of the conversion. Must be between 0 and 1. /// The . - public static Image Grayscale(this Image source, GrayscaleMode mode) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount) where TPixel : struct, IPixel { - return Grayscale(source, mode, source.Bounds); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor(amount) + : new GrayscaleBt601Processor(1F); + + source.ApplyProcessor(processor); + return source; } /// @@ -50,14 +73,23 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Grayscale(this Image source, Rectangle rectangle) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - IImageProcessor processor = new GrayscaleBt709Processor(); + => Grayscale(source, 1F, rectangle); - source.ApplyProcessor(processor, rectangle); - return source; - } + /// + /// Applies Grayscale toning to the image using the given amount. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be between 0 and 1. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle) + where TPixel : struct, IPixel + => Grayscale(source, GrayscaleMode.Bt709, amount, rectangle); /// /// Applies Grayscale toning to the image. @@ -69,15 +101,30 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Grayscale(this Image source, GrayscaleMode mode, Rectangle rectangle) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) + where TPixel : struct, IPixel + => Grayscale(source, mode, 1F, rectangle); + + /// + /// Applies Grayscale toning to the image using the given amount. + /// + /// The pixel format. + /// The image this method extends. + /// The formula to apply to perform the operation. + /// The proportion of the conversion. Must be between 0 and 1. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle) where TPixel : struct, IPixel { IImageProcessor processor = mode == GrayscaleMode.Bt709 - ? (IImageProcessor)new GrayscaleBt709Processor() - : new GrayscaleBt601Processor(); + ? (IImageProcessor)new GrayscaleBt709Processor(amount) + : new GrayscaleBt601Processor(amount); source.ApplyProcessor(processor, rectangle); return source; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ColorMatrix/Hue.cs b/src/ImageSharp/Processing/ColorMatrix/Hue.cs index d218b3a104..76af10c369 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Hue.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Hue.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,12 +19,13 @@ namespace ImageSharp /// /// The pixel format. /// The image this method extends. - /// The angle in degrees to adjust the image. + /// The rotation angle in degrees to adjust the hue. /// The . - public static Image Hue(this Image source, float degrees) + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) where TPixel : struct, IPixel { - return Hue(source, degrees, source.Bounds); + source.ApplyProcessor(new HueProcessor(degrees)); + return source; } /// @@ -36,16 +33,16 @@ namespace ImageSharp /// /// The pixel format. /// The image this method extends. - /// The angle in degrees to adjust the image. + /// The rotation angle in degrees to adjust the hue. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Hue(this Image source, float degrees, Rectangle rectangle) + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new HueProcessor(degrees), rectangle); return source; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs index 09eb131907..d7845320ad 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,11 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Kodachrome(this Image source) + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) where TPixel : struct, IPixel { - return Kodachrome(source, source.Bounds); + source.ApplyProcessor(new KodachromeProcessor()); + return source; } /// @@ -39,7 +36,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Kodachrome(this Image source, Rectangle rectangle) + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new KodachromeProcessor(), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs index bca4577e48..947e531578 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,10 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Lomograph(this Image source) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source) where TPixel : struct, IPixel { - return Lomograph(source, source.Bounds, GraphicsOptions.Default); + return Lomograph(source, GraphicsOptions.Default); } /// @@ -39,7 +35,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Lomograph(this Image source, Rectangle rectangle) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return Lomograph(source, rectangle, GraphicsOptions.Default); @@ -52,10 +48,11 @@ namespace ImageSharp /// The image this method extends. /// The options effecting pixel blending. /// The . - public static Image Lomograph(this Image source, GraphicsOptions options) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - return Lomograph(source, source.Bounds, options); + source.ApplyProcessor(new LomographProcessor(options)); + return source; } /// @@ -68,7 +65,7 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Lomograph(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { source.ApplyProcessor(new LomographProcessor(options), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs b/src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs new file mode 100644 index 0000000000..8cbc21b2a6 --- /dev/null +++ b/src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs @@ -0,0 +1,446 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Provides extensions methods for the struct + /// + public static class MatrixFilters + { + /// + /// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness + /// + public static Matrix4x4 AchromatomalyFilter { get; } = new Matrix4x4 + { + M11 = .618F, + M12 = .163F, + M13 = .163F, + M21 = .320F, + M22 = .775F, + M23 = .320F, + M31 = .062F, + M32 = .062F, + M33 = .516F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Achromatopsia (Monochrome) color blindness. + /// + public static Matrix4x4 AchromatopsiaFilter { get; } = new Matrix4x4 + { + M11 = .299F, + M12 = .299F, + M13 = .299F, + M21 = .587F, + M22 = .587F, + M23 = .587F, + M31 = .114F, + M32 = .114F, + M33 = .114F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness. + /// + public static Matrix4x4 DeuteranomalyFilter { get; } = new Matrix4x4 + { + M11 = 0.8F, + M12 = 0.258F, + M21 = 0.2F, + M22 = 0.742F, + M23 = 0.142F, + M33 = 0.858F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Deuteranopia (Green-Blind) color blindness. + /// + public static Matrix4x4 DeuteranopiaFilter { get; } = new Matrix4x4 + { + M11 = 0.625F, + M12 = 0.7F, + M21 = 0.375F, + M22 = 0.3F, + M23 = 0.3F, + M33 = 0.7F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Protanomaly (Red-Weak) color blindness. + /// + public static Matrix4x4 ProtanomalyFilter { get; } = new Matrix4x4 + { + M11 = 0.817F, + M12 = 0.333F, + M21 = 0.183F, + M22 = 0.667F, + M23 = 0.125F, + M33 = 0.875F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Protanopia (Red-Blind) color blindness. + /// + public static Matrix4x4 ProtanopiaFilter { get; } = new Matrix4x4 + { + M11 = 0.567F, + M12 = 0.558F, + M21 = 0.433F, + M22 = 0.442F, + M23 = 0.242F, + M33 = 0.758F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness. + /// + public static Matrix4x4 TritanomalyFilter { get; } = new Matrix4x4 + { + M11 = 0.967F, + M21 = 0.33F, + M22 = 0.733F, + M23 = 0.183F, + M32 = 0.267F, + M33 = 0.817F, + M44 = 1 + }; + + /// + /// Gets a filter recreating Tritanopia (Blue-Blind) color blindness. + /// + public static Matrix4x4 TritanopiaFilter { get; } = new Matrix4x4 + { + M11 = 0.95F, + M21 = 0.05F, + M22 = 0.433F, + M23 = 0.475F, + M32 = 0.567F, + M33 = 0.525F, + M44 = 1 + }; + + /// + /// Gets an approximated black and white filter + /// + public static Matrix4x4 BlackWhiteFilter { get; } = new Matrix4x4() + { + M11 = 1.5F, + M12 = 1.5F, + M13 = 1.5F, + M21 = 1.5F, + M22 = 1.5F, + M23 = 1.5F, + M31 = 1.5F, + M32 = 1.5F, + M33 = 1.5F, + M41 = -1F, + M42 = -1F, + M43 = -1F, + M44 = 1 + }; + + /// + /// Gets a filter recreating an old Kodachrome camera effect. + /// + public static Matrix4x4 KodachromeFilter { get; } = new Matrix4x4 + { + M11 = 0.7297023F, + M22 = 0.6109577F, + M33 = 0.597218F, + M41 = 0.105F, + M42 = 0.145F, + M43 = 0.155F, + M44 = 1 + } + + * CreateSaturateFilter(1.2F) * CreateContrastFilter(1.35F); + + /// + /// Gets a filter recreating an old Lomograph camera effect. + /// + public static Matrix4x4 LomographFilter { get; } = new Matrix4x4 + { + M11 = 1.5F, + M22 = 1.45F, + M33 = 1.16F, + M41 = -.1F, + M42 = -.02F, + M43 = -.07F, + M44 = 1 + } + + * CreateSaturateFilter(1.1F) * CreateContrastFilter(1.33F); + + /// + /// Gets a filter recreating an old Polaroid camera effect. + /// + public static Matrix4x4 PolaroidFilter { get; } = new Matrix4x4 + { + M11 = 1.538F, + M12 = -0.062F, + M13 = -0.262F, + M21 = -0.022F, + M22 = 1.578F, + M23 = -0.022F, + M31 = .216F, + M32 = -.16F, + M33 = 1.5831F, + M41 = 0.02F, + M42 = -0.05F, + M43 = -0.05F, + M44 = 1 + }; + + /// + /// Create a brightness filter matrix using the given amount. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The + public static Matrix4x4 CreateBrightnessFilter(float amount) + { + Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); + + // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + return new Matrix4x4 + { + M11 = amount, + M22 = amount, + M33 = amount, + M44 = 1 + }; + } + + /// + /// Create a contrast filter matrix using the given amount. + /// + /// + /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The + public static Matrix4x4 CreateContrastFilter(float amount) + { + Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); + + // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + float contrast = (-.5F * amount) + .5F; + + return new Matrix4x4 + { + M11 = amount, + M22 = amount, + M33 = amount, + M41 = contrast, + M42 = contrast, + M43 = contrast, + M44 = 1 + }; + } + + /// + /// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.601. + /// + /// + /// The proportion of the conversion. Must be between 0 and 1. + /// The + public static Matrix4x4 CreateGrayscaleBt601Filter(float amount) + { + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + amount = 1F - amount; + + // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + return new Matrix4x4 + { + M11 = .299F + (.701F * amount), + M12 = .299F - (.299F * amount), + M13 = .299F - (.299F * amount), + M21 = .587F - (.587F * amount), + M22 = .587F + (.413F * amount), + M23 = .587F - (.587F * amount), + M31 = .114F - (.114F * amount), + M32 = .114F - (.114F * amount), + M33 = .114F + (.886F * amount), + M44 = 1 + }; + } + + /// + /// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.709. + /// + /// + /// The proportion of the conversion. Must be between 0 and 1. + /// The + public static Matrix4x4 CreateGrayscaleBt709Filter(float amount) + { + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + amount = 1F - amount; + + // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + return new Matrix4x4 + { + M11 = .2126F + (.7874F * amount), + M12 = .2126F - (.2126F * amount), + M13 = .2126F - (.2126F * amount), + M21 = .7152F - (.7152F * amount), + M22 = .7152F + (.2848F * amount), + M23 = .7152F - (.7152F * amount), + M31 = .0722F - (.0722F * amount), + M32 = .0722F - (.0722F * amount), + M33 = .0722F + (.9278F * amount), + M44 = 1 + }; + } + + /// + /// Create a hue filter matrix using the given angle in degrees. + /// + /// The angle of rotation in degrees. + /// The + public static Matrix4x4 CreateHueFilter(float degrees) + { + // Wrap the angle round at 360. + degrees = degrees % 360; + + // Make sure it's not negative. + while (degrees < 0) + { + degrees += 360; + } + + float radian = MathFExtensions.DegreeToRadian(degrees); + float cosRadian = MathF.Cos(radian); + float sinRadian = MathF.Sin(radian); + + // The matrix is set up to preserve the luminance of the image. + // See http://graficaobscura.com/matrix/index.html + // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx + return new Matrix4x4 + { + M11 = .213F + (cosRadian * .787F) - (sinRadian * .213F), + M12 = .213F - (cosRadian * .213F) - (sinRadian * 0.143F), + M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F), + M21 = .715F - (cosRadian * .715F) - (sinRadian * .715F), + M22 = .715F + (cosRadian * .285F) + (sinRadian * 0.140F), + M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F), + M31 = .072F - (cosRadian * .072F) + (sinRadian * .928F), + M32 = .072F - (cosRadian * .072F) - (sinRadian * 0.283F), + M33 = .072F + (cosRadian * .928F) + (sinRadian * .072F), + M44 = 1 + }; + } + + /// + /// Create an invert filter matrix using the given amount. + /// + /// The proportion of the conversion. Must be between 0 and 1. + /// The + public static Matrix4x4 CreateInvertFilter(float amount) + { + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + + // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + float invert = 1F - (2F * amount); + + return new Matrix4x4 + { + M11 = invert, + M22 = invert, + M33 = invert, + M41 = amount, + M42 = amount, + M43 = amount, + M44 = 1 + }; + } + + /// + /// Create an opacity filter matrix using the given amount. + /// + /// The proportion of the conversion. Must be between 0 and 1. + /// The + public static Matrix4x4 CreateOpacityFilter(float amount) + { + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + + // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + return new Matrix4x4 + { + M11 = 1, + M22 = 1, + M33 = 1, + M44 = amount + }; + } + + /// + /// Create a saturation filter matrix using the given amount. + /// + /// + /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The + public static Matrix4x4 CreateSaturateFilter(float amount) + { + Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); + + // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + return new Matrix4x4 + { + M11 = .213F + (.787F * amount), + M12 = .213F - (.213F * amount), + M13 = .213F - (.213F * amount), + M21 = .715F - (.715F * amount), + M22 = .715F + (.285F * amount), + M23 = .715F - (.715F * amount), + M31 = 1F - ((.213F + (.787F * amount)) + (.715F - (.715F * amount))), + M32 = 1F - ((.213F - (.213F * amount)) + (.715F + (.285F * amount))), + M33 = 1F - ((.213F - (.213F * amount)) + (.715F - (.715F * amount))), + M44 = 1 + }; + } + + /// + /// Create a sepia filter matrix using the given amount. + /// The formula used matches the svg specification. + /// + /// The proportion of the conversion. Must be between 0 and 1. + /// The + public static Matrix4x4 CreateSepiaFilter(float amount) + { + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + amount = 1F - amount; + + // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc + return new Matrix4x4 + { + M11 = .393F + (.607F * amount), + M12 = .349F - (.349F * amount), + M13 = .272F - (.272F * amount), + M21 = .769F - (.769F * amount), + M22 = .686F + (.314F * amount), + M23 = .534F - (.534F * amount), + M31 = .189F - (.189F * amount), + M32 = .168F - (.168F * amount), + M33 = .131F + (.869F * amount), + M44 = 1 + }; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Effects/Alpha.cs b/src/ImageSharp/Processing/ColorMatrix/Opacity.cs similarity index 55% rename from src/ImageSharp/Processing/Effects/Alpha.cs rename to src/ImageSharp/Processing/ColorMatrix/Opacity.cs index a0d218651a..b310b4b915 100644 --- a/src/ImageSharp/Processing/Effects/Alpha.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Opacity.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -22,29 +18,24 @@ namespace ImageSharp /// /// The pixel format. /// The image this method extends. - /// The new opacity of the image. Must be between 0 and 1. + /// The proportion of the conversion. Must be between 0 and 1. /// The . - public static Image Alpha(this Image source, float percent) + public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) where TPixel : struct, IPixel - { - return Alpha(source, percent, source.Bounds); - } + => source.ApplyProcessor(new OpacityProcessor(amount)); /// /// Alters the alpha component of the image. /// /// The pixel format. /// The image this method extends. - /// The new opacity of the image. Must be between 0 and 1. + /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Alpha(this Image source, float percent, Rectangle rectangle) + public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new AlphaProcessor(percent), rectangle); - return source; - } + => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs b/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs index def253234f..1b92029f6b 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the various types of defined color blindness filters. diff --git a/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs b/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs index f1294ffaff..370071b7ab 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the various types of defined Grayscale filters. diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs index b2d8515d6c..c96087d57e 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Polaroid(this Image source) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source) where TPixel : struct, IPixel { return Polaroid(source, GraphicsOptions.Default); @@ -39,7 +35,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Polaroid(this Image source, Rectangle rectangle) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return Polaroid(source, rectangle, GraphicsOptions.Default); @@ -52,10 +48,11 @@ namespace ImageSharp /// The image this method extends. /// The options effecting pixel blending. /// The . - public static Image Polaroid(this Image source, GraphicsOptions options) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - return Polaroid(source, source.Bounds, options); + source.ApplyProcessor(new PolaroidProcessor(options)); + return source; } /// @@ -68,7 +65,7 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Polaroid(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { source.ApplyProcessor(new PolaroidProcessor(options), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturate.cs b/src/ImageSharp/Processing/ColorMatrix/Saturate.cs new file mode 100644 index 0000000000..c7dd395aa3 --- /dev/null +++ b/src/ImageSharp/Processing/ColorMatrix/Saturate.cs @@ -0,0 +1,54 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the saturation component of the image. + /// + /// + /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The . + public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new SaturateProcessor(amount)); + return source; + } + + /// + /// Alters the saturation component of the image. + /// + /// + /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new SaturateProcessor(amount), rectangle); + return source; + } + } +} diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs deleted file mode 100644 index 88f3b15296..0000000000 --- a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Alters the saturation component of the image. - /// - /// The pixel format. - /// The image this method extends. - /// The new saturation of the image. Must be between -100 and 100. - /// The . - public static Image Saturation(this Image source, int amount) - where TPixel : struct, IPixel - { - return Saturation(source, amount, source.Bounds); - } - - /// - /// Alters the saturation component of the image. - /// - /// The pixel format. - /// The image this method extends. - /// The new saturation of the image. Must be between -100 and 100. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The . - public static Image Saturation(this Image source, int amount, Rectangle rectangle) - where TPixel : struct, IPixel - { - source.ApplyProcessor(new SaturationProcessor(amount), rectangle); - return source; - } - } -} diff --git a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs index 000c0ffba8..0d686f4dba 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs @@ -1,18 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,26 +18,46 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Sepia(this Image source) + public static IImageProcessingContext Sepia(this IImageProcessingContext source) + where TPixel : struct, IPixel + => Sepia(source, 1F); + + /// + /// Applies sepia toning to the image using the given amount. + /// + /// The pixel format. + /// The image this method extends. + /// The proportion of the conversion. Must be between 0 and 1. + /// The . + public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) + where TPixel : struct, IPixel + => source.ApplyProcessor(new SepiaProcessor(amount)); + + /// + /// Applies sepia toning to the image. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - return Sepia(source, source.Bounds); - } + => Sepia(source, 1F, rectangle); /// /// Applies sepia toning to the image. /// /// The pixel format. /// The image this method extends. + /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Sepia(this Image source, Rectangle rectangle) + public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new SepiaProcessor(), rectangle); - return source; - } + => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/BoxBlur.cs b/src/ImageSharp/Processing/Convolution/BoxBlur.cs index ad5e477dc9..b0c6ffc8d2 100644 --- a/src/ImageSharp/Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp/Processing/Convolution/BoxBlur.cs @@ -1,22 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Applies a box blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BoxBlurProcessor(7)); + /// /// Applies a box blur to the image. /// @@ -24,11 +30,9 @@ namespace ImageSharp /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The . - public static Image BoxBlur(this Image source, int radius = 7) + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) where TPixel : struct, IPixel - { - return BoxBlur(source, radius, source.Bounds); - } + => source.ApplyProcessor(new BoxBlurProcessor(radius)); /// /// Applies a box blur to the image. @@ -40,11 +44,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); - return source; - } + => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } } diff --git a/src/ImageSharp/Processing/Convolution/DetectEdges.cs b/src/ImageSharp/Processing/Convolution/DetectEdges.cs index 1cc8b693ff..79e7ac6cf4 100644 --- a/src/ImageSharp/Processing/Convolution/DetectEdges.cs +++ b/src/ImageSharp/Processing/Convolution/DetectEdges.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,10 +21,10 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image DetectEdges(this Image source) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) where TPixel : struct, IPixel { - return DetectEdges(source, source.Bounds, new SobelProcessor { Grayscale = true }); + return DetectEdges(source, new SobelProcessor { Grayscale = true }); } /// @@ -41,12 +37,23 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return DetectEdges(source, rectangle, new SobelProcessor { Grayscale = true }); } + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The image this method extends. + /// The filter for detecting edges. + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetection filter) + where TPixel : struct, IPixel + => DetectEdges(source, GetProcessor(filter, true)); + /// /// Detects any edges within the image. /// @@ -55,11 +62,9 @@ namespace ImageSharp /// The filter for detecting edges. /// Whether to convert the image to Grayscale first. Defaults to true. /// The . - public static Image DetectEdges(this Image source, EdgeDetection filter, bool grayscale = true) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetection filter, bool grayscale) where TPixel : struct, IPixel - { - return DetectEdges(source, filter, source.Bounds, grayscale); - } + => DetectEdges(source, GetProcessor(filter, grayscale)); /// /// Detects any edges within the image. @@ -72,7 +77,41 @@ namespace ImageSharp /// /// Whether to convert the image to Grayscale first. Defaults to true. /// The . - public static Image DetectEdges(this Image source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) + where TPixel : struct, IPixel + => DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); + + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The image this method extends. + /// The filter for detecting edges. + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IEdgeDetectorProcessor filter) + where TPixel : struct, IPixel + { + return source.ApplyProcessor(filter); + } + + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The filter for detecting edges. + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IEdgeDetectorProcessor filter) + where TPixel : struct, IPixel + { + source.ApplyProcessor(filter, rectangle); + return source; + } + + private static IEdgeDetectorProcessor GetProcessor(EdgeDetection filter, bool grayscale) where TPixel : struct, IPixel { IEdgeDetectorProcessor processor; @@ -120,37 +159,7 @@ namespace ImageSharp break; } - return DetectEdges(source, rectangle, processor); - } - - /// - /// Detects any edges within the image. - /// - /// The pixel format. - /// The image this method extends. - /// The filter for detecting edges. - /// The . - public static Image DetectEdges(this Image source, IEdgeDetectorProcessor filter) - where TPixel : struct, IPixel - { - return DetectEdges(source, source.Bounds, filter); - } - - /// - /// Detects any edges within the image. - /// - /// The pixel format. - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The filter for detecting edges. - /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorProcessor filter) - where TPixel : struct, IPixel - { - source.ApplyProcessor(filter, rectangle); - return source; + return processor; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs index f9658fcb5d..9bca97242c 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs @@ -1,23 +1,29 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Applies a Gaussian blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GaussianBlurProcessor(3f)); + /// /// Applies a Gaussian blur to the image. /// @@ -25,11 +31,9 @@ namespace ImageSharp /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static Image GaussianBlur(this Image source, float sigma = 3f) + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) where TPixel : struct, IPixel - { - return GaussianBlur(source, sigma, source.Bounds); - } + => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); /// /// Applies a Gaussian blur to the image. @@ -41,11 +45,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image GaussianBlur(this Image source, float sigma, Rectangle rectangle) + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); - return source; - } + => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs index bb616cc67e..1cea2dae9b 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs @@ -1,23 +1,29 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Applies a Gaussian sharpening filter to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GaussianSharpenProcessor(3f)); + /// /// Applies a Gaussian sharpening filter to the image. /// @@ -25,11 +31,9 @@ namespace ImageSharp /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static Image GaussianSharpen(this Image source, float sigma = 3f) + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) where TPixel : struct, IPixel - { - return GaussianSharpen(source, sigma, source.Bounds); - } + => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter to the image. @@ -41,11 +45,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image GaussianSharpen(this Image source, float sigma, Rectangle rectangle) + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); - return source; - } + => source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); } } diff --git a/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs b/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs index 809992f005..b01bb945cf 100644 --- a/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs +++ b/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the various types of defined edge detection filters. diff --git a/src/ImageSharp/Processing/Delegate.cs b/src/ImageSharp/Processing/Delegate.cs new file mode 100644 index 0000000000..b390e46ae9 --- /dev/null +++ b/src/ImageSharp/Processing/Delegate.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies the given operation to the mutable image. + /// Useful when we need to extract information like Width/Height to parameterize the next operation working on the chain. + /// To achieve this the method actually implements an "inline" with as it's processing logic. + /// + /// The pixel format. + /// The image to mutate. + /// The operation to perform on the source. + /// The to allow chaining of operations. + public static IImageProcessingContext Apply(this IImageProcessingContext source, Action> operation) + where TPixel : struct, IPixel + => source.ApplyProcessor(new DelegateProcessor(operation)); + } +} diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs index a1e04c8a3d..da00b4ddd0 100644 --- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,11 +21,9 @@ namespace ImageSharp /// The color to set as the background. /// The options effecting pixel blending. /// The . - public static Image BackgroundColor(this Image source, TPixel color, GraphicsOptions options) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - { - return BackgroundColor(source, color, source.Bounds, options); - } + => source.ApplyProcessor(new BackgroundColorProcessor(color, options)); /// /// Replaces the background color of image with the given one. @@ -42,12 +36,9 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); - return source; - } + => source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); /// /// Replaces the background color of image with the given one. @@ -56,7 +47,7 @@ namespace ImageSharp /// The image this method extends. /// The color to set as the background. /// The . - public static Image BackgroundColor(this Image source, TPixel color) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return BackgroundColor(source, color, GraphicsOptions.Default); @@ -72,7 +63,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle) where TPixel : struct, IPixel { return BackgroundColor(source, color, rectangle, GraphicsOptions.Default); diff --git a/src/ImageSharp/Processing/Effects/Brightness.cs b/src/ImageSharp/Processing/Effects/Brightness.cs deleted file mode 100644 index 165f897b80..0000000000 --- a/src/ImageSharp/Processing/Effects/Brightness.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Alters the brightness component of the image. - /// - /// The pixel format. - /// The image this method extends. - /// The new brightness of the image. Must be between -100 and 100. - /// The . - public static Image Brightness(this Image source, int amount) - where TPixel : struct, IPixel - { - return Brightness(source, amount, source.Bounds); - } - - /// - /// Alters the brightness component of the image. - /// - /// The pixel format. - /// The image this method extends. - /// The new brightness of the image. Must be between -100 and 100. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The . - public static Image Brightness(this Image source, int amount, Rectangle rectangle) - where TPixel : struct, IPixel - { - source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); - return source; - } - } -} diff --git a/src/ImageSharp/Processing/Effects/Invert.cs b/src/ImageSharp/Processing/Effects/Invert.cs index d9a0695566..7dd9ed3dd7 100644 --- a/src/ImageSharp/Processing/Effects/Invert.cs +++ b/src/ImageSharp/Processing/Effects/Invert.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,11 +19,9 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Invert(this Image source) + public static IImageProcessingContext Invert(this IImageProcessingContext source) where TPixel : struct, IPixel - { - return Invert(source, source.Bounds); - } + => source.ApplyProcessor(new InvertProcessor(1F)); /// /// Inverts the colors of the image. @@ -38,11 +32,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Invert(this Image source, Rectangle rectangle) + public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new InvertProcessor(), rectangle); - return source; - } + => source.ApplyProcessor(new InvertProcessor(1F), rectangle); } } diff --git a/src/ImageSharp/Processing/Effects/OilPainting.cs b/src/ImageSharp/Processing/Effects/OilPainting.cs index 3b300e9197..0494c9a8b9 100644 --- a/src/ImageSharp/Processing/Effects/OilPainting.cs +++ b/src/ImageSharp/Processing/Effects/OilPainting.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image OilPaint(this Image source) + public static IImageProcessingContext OilPaint(this IImageProcessingContext source) where TPixel : struct, IPixel { return OilPaint(source, 10, 15); @@ -40,7 +36,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image OilPaint(this Image source, Rectangle rectangle) + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return OilPaint(source, 10, 15, rectangle); @@ -54,11 +50,9 @@ namespace ImageSharp /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. /// The . - public static Image OilPaint(this Image source, int levels, int brushSize) - where TPixel : struct, IPixel - { - return OilPaint(source, levels, brushSize, source.Bounds); - } + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) + where TPixel : struct, IPixel + => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); /// /// Alters the colors of the image recreating an oil painting effect. @@ -71,18 +65,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image OilPaint(this Image source, int levels, int brushSize, Rectangle rectangle) + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize, Rectangle rectangle) where TPixel : struct, IPixel - { - Guard.MustBeGreaterThan(levels, 0, nameof(levels)); - - if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(brushSize)); - } - - source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); - return source; - } + => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Effects/Pixelate.cs b/src/ImageSharp/Processing/Effects/Pixelate.cs index 07fdd50a35..29e348f6e6 100644 --- a/src/ImageSharp/Processing/Effects/Pixelate.cs +++ b/src/ImageSharp/Processing/Effects/Pixelate.cs @@ -1,22 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Pixelates an image with the given pixel size. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext Pixelate(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new PixelateProcessor(4)); + /// /// Pixelates an image with the given pixel size. /// @@ -24,11 +30,9 @@ namespace ImageSharp /// The image this method extends. /// The size of the pixels. /// The . - public static Image Pixelate(this Image source, int size = 4) + public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) where TPixel : struct, IPixel - { - return Pixelate(source, size, source.Bounds); - } + => source.ApplyProcessor(new PixelateProcessor(size)); /// /// Pixelates an image with the given pixel size. @@ -40,16 +44,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Pixelate(this Image source, int size, Rectangle rectangle) + public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size, Rectangle rectangle) where TPixel : struct, IPixel - { - if (size <= 0 || size > source.Height || size > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(size)); - } - - source.ApplyProcessor(new PixelateProcessor(size), rectangle); - return source; - } + => source.ApplyProcessor(new PixelateProcessor(size), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/ImageProcessor.cs b/src/ImageSharp/Processing/ImageProcessor.cs deleted file mode 100644 index dd2a93bc52..0000000000 --- a/src/ImageSharp/Processing/ImageProcessor.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// Allows the application of processors to images. - /// - /// The pixel format. - internal abstract class ImageProcessor : IImageProcessor - where TPixel : struct, IPixel - { - /// - public virtual ParallelOptions ParallelOptions { get; set; } - - /// - public virtual bool Compand { get; set; } = false; - - /// - public void Apply(ImageBase source, Rectangle sourceRectangle) - { - if (this.ParallelOptions == null) - { - this.ParallelOptions = source.Configuration.ParallelOptions; - } - - try - { - this.BeforeApply(source, sourceRectangle); - - this.OnApply(source, sourceRectangle); - - this.AfterApply(source, sourceRectangle); - } -#if DEBUG - catch (Exception) - { - throw; -#else - catch (Exception ex) - { - throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); -#endif - } - } - - /// - /// This method is called before the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void BeforeApply(ImageBase source, Rectangle sourceRectangle) - { - } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected abstract void OnApply(ImageBase source, Rectangle sourceRectangle); - - /// - /// This method is called after the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void AfterApply(ImageBase source, Rectangle sourceRectangle) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index 84f6bf10ae..ee35963487 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -21,7 +18,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Glow(this Image source) + public static IImageProcessingContext Glow(this IImageProcessingContext source) where TPixel : struct, IPixel { return Glow(source, GraphicsOptions.Default); @@ -34,7 +31,7 @@ namespace ImageSharp /// The image this method extends. /// The color to set as the glow. /// The . - public static Image Glow(this Image source, TPixel color) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return Glow(source, color, GraphicsOptions.Default); @@ -47,7 +44,7 @@ namespace ImageSharp /// The image this method extends. /// The the radius. /// The . - public static Image Glow(this Image source, float radius) + public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) where TPixel : struct, IPixel { return Glow(source, radius, GraphicsOptions.Default); @@ -62,11 +59,9 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Glow(this Image source, Rectangle rectangle) + public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - return Glow(source, rectangle, GraphicsOptions.Default); - } + => source.Glow(rectangle, GraphicsOptions.Default); /// /// Applies a radial glow effect to an image. @@ -79,11 +74,9 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle) where TPixel : struct, IPixel - { - return Glow(source, color, radius, rectangle, GraphicsOptions.Default); - } + => source.Glow(color, ValueSize.Absolute(radius), rectangle, GraphicsOptions.Default); /// /// Applies a radial glow effect to an image. @@ -92,11 +85,9 @@ namespace ImageSharp /// The image this method extends. /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds, options); - } + => source.Glow(NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), options); /// /// Applies a radial glow effect to an image. @@ -106,11 +97,9 @@ namespace ImageSharp /// The color to set as the glow. /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, TPixel color, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, color, source.Bounds.Width * .5F, source.Bounds, options); - } + => source.Glow(color, ValueSize.PercentageOfWidth(0.5f), options); /// /// Applies a radial glow effect to an image. @@ -120,11 +109,9 @@ namespace ImageSharp /// The the radius. /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, float radius, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, NamedColors.Black, radius, source.Bounds, options); - } + => source.Glow(NamedColors.Black, ValueSize.Absolute(radius), options); /// /// Applies a radial glow effect to an image. @@ -136,11 +123,9 @@ namespace ImageSharp /// /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, NamedColors.Black, 0, rectangle, options); - } + => source.Glow(NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), rectangle, options); /// /// Applies a radial glow effect to an image. @@ -154,12 +139,37 @@ namespace ImageSharp /// /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - var processor = new GlowProcessor(color, options) { Radius = radius, }; - source.ApplyProcessor(processor, rectangle); - return source; - } + => source.Glow(color, ValueSize.Absolute(radius), rectangle, options); + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the glow. + /// The the radius. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The options effecting things like blending. + /// The . + private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the glow. + /// The the radius. + /// The options effecting things like blending. + /// The . + private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GlowProcessor(color, radius, options)); } } diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs index 75c4611a1e..cc93ccedc6 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -21,7 +18,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Vignette(this Image source) + public static IImageProcessingContext Vignette(this IImageProcessingContext source) where TPixel : struct, IPixel { return Vignette(source, GraphicsOptions.Default); @@ -34,7 +31,7 @@ namespace ImageSharp /// The image this method extends. /// The color to set as the vignette. /// The . - public static Image Vignette(this Image source, TPixel color) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return Vignette(source, color, GraphicsOptions.Default); @@ -48,7 +45,7 @@ namespace ImageSharp /// The the x-radius. /// The the y-radius. /// The . - public static Image Vignette(this Image source, float radiusX, float radiusY) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY) where TPixel : struct, IPixel { return Vignette(source, radiusX, radiusY, GraphicsOptions.Default); @@ -63,7 +60,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Vignette(this Image source, Rectangle rectangle) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return Vignette(source, rectangle, GraphicsOptions.Default); @@ -81,11 +78,9 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) where TPixel : struct, IPixel - { - return Vignette(source, color, radiusX, radiusY, rectangle, GraphicsOptions.Default); - } + => source.Vignette(color, radiusX, radiusY, rectangle, GraphicsOptions.Default); /// /// Applies a radial vignette effect to an image. @@ -94,11 +89,9 @@ namespace ImageSharp /// The image this method extends. /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options); - } + => source.VignetteInternal(NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), options); /// /// Applies a radial vignette effect to an image. @@ -108,11 +101,9 @@ namespace ImageSharp /// The color to set as the vignette. /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, TPixel color, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options); - } + => source.VignetteInternal(color, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), options); /// /// Applies a radial vignette effect to an image. @@ -123,11 +114,9 @@ namespace ImageSharp /// The the y-radius. /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, float radiusX, float radiusY, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds, options); - } + => source.VignetteInternal(NamedColors.Black, radiusX, radiusY, options); /// /// Applies a radial vignette effect to an image. @@ -139,11 +128,9 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, NamedColors.Black, 0, 0, rectangle, options); - } + => source.VignetteInternal(NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), rectangle, options); /// /// Applies a radial vignette effect to an image. @@ -158,12 +145,16 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - var processor = new VignetteProcessor(color, options) { RadiusX = radiusX, RadiusY = radiusY }; - source.ApplyProcessor(processor, rectangle); - return source; - } + => source.VignetteInternal(color, radiusX, radiusY, rectangle, options); + + private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); + + private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index ea1b759ab5..434ed02698 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to perform binary threshold filtering against an /// . The image will be converted to grayscale before thresholding occurs. @@ -50,13 +48,13 @@ namespace ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float threshold = this.Threshold; TPixel upper = this.UpperColor; @@ -87,10 +85,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span row = source.GetRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { diff --git a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index 85522e2886..01cba15c4b 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that dithers an image using error diffusion. /// @@ -56,47 +55,29 @@ namespace ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; - for (int y = minY; y < maxY; y++) + for (int y = startY; y < endY; y++) { - int offsetY = y - startY; - Span row = source.GetRowSpan(offsetY); + Span row = source.GetPixelRowSpan(y); - for (int x = minX; x < maxX; x++) + for (int x = startX; x < endX; x++) { - int offsetX = x - startX; - TPixel sourceColor = row[offsetX]; + TPixel sourceColor = row[x]; TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor; - this.Diffuser.Dither(source, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY); + this.Diffuser.Dither(source, sourceColor, transformedColor, x, y, startX, startY, endX, endY); } } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs index a190bcd61b..a37d12f18c 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Buffers; - - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Buffers; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that dithers an image using error diffusion. /// @@ -64,47 +63,29 @@ namespace ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - byte[] bytes = new byte[4]; - for (int y = minY; y < maxY; y++) + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + var rgba = default(Rgba32); + for (int y = startY; y < endY; y++) { - int offsetY = y - startY; - Span row = source.GetRowSpan(offsetY); + Span row = source.GetPixelRowSpan(y); - for (int x = minX; x < maxX; x++) + for (int x = startX; x < endX; x++) { - int offsetX = x - startX; - TPixel sourceColor = row[offsetX]; - this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY); + TPixel sourceColor = row[x]; + this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, ref rgba, this.Index, x, y); } } } diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs new file mode 100644 index 0000000000..4672b2ad45 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -0,0 +1,141 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Allows the application of processors to images. + /// + /// The pixel format. + internal abstract class CloningImageProcessor : ICloningImageProcessor + where TPixel : struct, IPixel + { + /// + public Image CloneAndApply(Image source, Rectangle sourceRectangle) + { + try + { + Image clone = this.CreateDestination(source, sourceRectangle); + + if (clone.Frames.Count != source.Frames.Count) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); + } + + Configuration configuration = source.GetConfiguration(); + this.BeforeImageApply(source, clone, sourceRectangle); + + for (int i = 0; i < source.Frames.Count; i++) + { + ImageFrame sourceFrame = source.Frames[i]; + ImageFrame clonedFrame = clone.Frames[i]; + + this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + this.OnApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + this.AfterApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + } + + this.AfterImageApply(source, clone, sourceRectangle); + + return clone; + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + public void Apply(Image source, Rectangle sourceRectangle) + { + using (Image cloned = this.CloneAndApply(source, sourceRectangle)) + { + // we now need to move the pixel data/size data from one image base to another + if (cloned.Frames.Count != source.Frames.Count) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); + } + + source.SwapPixelsBuffers(cloned); + } + } + + /// + /// Generates a deep clone of the source image that operations should be applied to. + /// + /// The source image. Cannot be null. + /// The source rectangle. + /// The cloned image. + protected virtual Image CreateDestination(Image source, Rectangle sourceRectangle) + { + return source.Clone(); + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + protected virtual void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected abstract void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration); + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + protected virtual void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs deleted file mode 100644 index d37d119a41..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image to their black and white equivalent. - /// - /// The pixel format. - internal class BlackWhiteProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 1.5F, - M12 = 1.5F, - M13 = 1.5F, - M21 = 1.5F, - M22 = 1.5F, - M23 = 1.5F, - M31 = 1.5F, - M32 = 1.5F, - M33 = 1.5F, - M41 = -1F, - M42 = -1F, - M43 = -1F, - M44 = 1 - }; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs deleted file mode 100644 index a91bd19467..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. - /// - /// The pixel format. - internal class AchromatomalyProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = .618F, - M12 = .163F, - M13 = .163F, - M21 = .320F, - M22 = .775F, - M23 = .320F, - M31 = .062F, - M32 = .062F, - M33 = .516F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs deleted file mode 100644 index d543c4edca..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. - /// - /// The pixel format. - internal class AchromatopsiaProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = .299F, - M12 = .299F, - M13 = .299F, - M21 = .587F, - M22 = .587F, - M23 = .587F, - M31 = .114F, - M32 = .114F, - M33 = .114F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs deleted file mode 100644 index ea73d0c662..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. - /// - /// The pixel format. - internal class DeuteranomalyProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.8F, - M12 = 0.258F, - M21 = 0.2F, - M22 = 0.742F, - M23 = 0.142F, - M33 = 0.858F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs deleted file mode 100644 index 4b5129a8bf..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. - /// - /// The pixel format. - internal class DeuteranopiaProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.625F, - M12 = 0.7F, - M21 = 0.375F, - M22 = 0.3F, - M23 = 0.3F, - M33 = 0.7F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs deleted file mode 100644 index 14eea08126..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness. - /// - /// The pixel format. - internal class ProtanomalyProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.817F, - M12 = 0.333F, - M21 = 0.183F, - M22 = 0.667F, - M23 = 0.125F, - M33 = 0.875F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs deleted file mode 100644 index 39cb715bde..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. - /// - /// The pixel format. - internal class ProtanopiaProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.567F, - M12 = 0.558F, - M21 = 0.433F, - M22 = 0.442F, - M23 = 0.242F, - M33 = 0.758F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs deleted file mode 100644 index 2b402197bc..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. - /// - /// The pixel format. - internal class TritanomalyProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.967F, - M21 = 0.33F, - M22 = 0.733F, - M23 = 0.183F, - M32 = 0.267F, - M33 = 0.817F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs deleted file mode 100644 index 5d228afa79..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. - /// - /// The pixel format. - internal class TritanopiaProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.95F, - M21 = 0.05F, - M22 = 0.433F, - M23 = 0.475F, - M32 = 0.567F, - M33 = 0.525F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs deleted file mode 100644 index 1bac145bc9..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// The color matrix filter. Inherit from this class to perform operation involving color matrices. - /// - /// The pixel format. - internal abstract class ColorMatrixProcessor : ImageProcessor, IColorMatrixFilter - where TPixel : struct, IPixel - { - /// - public abstract Matrix4x4 Matrix { get; } - - /// - public override bool Compand { get; set; } = true; - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - Matrix4x4 matrix = this.Matrix; - bool compand = this.Compand; - - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - Span row = source.GetRowSpan(y - startY); - - for (int x = minX; x < maxX; x++) - { - ref TPixel pixel = ref row[x - startX]; - var vector = pixel.ToVector4(); - - if (compand) - { - vector = vector.Expand(); - } - - vector = Vector4.Transform(vector, matrix); - pixel.PackFromVector4(compand ? vector.Compress() : vector); - } - }); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs deleted file mode 100644 index 65de8a66d2..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.601 - /// . - /// - /// The pixel format. - internal class GrayscaleBt601Processor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = .299F, - M12 = .299F, - M13 = .299F, - M21 = .587F, - M22 = .587F, - M23 = .587F, - M31 = .114F, - M32 = .114F, - M33 = .114F, - M44 = 1 - }; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs deleted file mode 100644 index 5f71e357b7..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709 - /// . - /// - /// The pixel format. - internal class GrayscaleBt709Processor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = .2126F, - M12 = .2126F, - M13 = .2126F, - M21 = .7152F, - M22 = .7152F, - M23 = .7152F, - M31 = .0722F, - M32 = .0722F, - M33 = .0722F, - M44 = 1 - }; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs deleted file mode 100644 index f286839776..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// An to change the hue of an . - /// - /// The pixel format. - internal class HueProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The new brightness of the image. Must be between -100 and 100. - public HueProcessor(float angle) - { - // Wrap the angle round at 360. - angle = angle % 360; - - // Make sure it's not negative. - while (angle < 0) - { - angle += 360; - } - - this.Angle = angle; - - float radians = MathF.DegreeToRadian(angle); - float cosradians = MathF.Cos(radians); - float sinradians = MathF.Sin(radians); - - float lumR = .213F; - float lumG = .715F; - float lumB = .072F; - - float oneMinusLumR = 1 - lumR; - float oneMinusLumG = 1 - lumG; - float oneMinusLumB = 1 - lumB; - - // The matrix is set up to preserve the luminance of the image. - // See http://graficaobscura.com/matrix/index.html - // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx - Matrix4x4 matrix4X4 = new Matrix4x4() - { - M11 = lumR + (cosradians * oneMinusLumR) - (sinradians * lumR), - M12 = lumR - (cosradians * lumR) - (sinradians * 0.143F), - M13 = lumR - (cosradians * lumR) - (sinradians * oneMinusLumR), - M21 = lumG - (cosradians * lumG) - (sinradians * lumG), - M22 = lumG + (cosradians * oneMinusLumG) + (sinradians * 0.140F), - M23 = lumG - (cosradians * lumG) + (sinradians * lumG), - M31 = lumB - (cosradians * lumB) + (sinradians * oneMinusLumB), - M32 = lumB - (cosradians * lumB) - (sinradians * 0.283F), - M33 = lumB + (cosradians * oneMinusLumB) + (sinradians * lumB), - M44 = 1 - }; - - this.Matrix = matrix4X4; - } - - /// - /// Gets the rotation value. - /// - public float Angle { get; } - - /// - public override Matrix4x4 Matrix { get; } - - /// - public override bool Compand => false; - } -} diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs deleted file mode 100644 index 0c29c65a10..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Encapsulates properties and methods for creating processors that utilize a matrix to - /// alter the image pixels. - /// - /// The pixel format. - internal interface IColorMatrixFilter : IImageProcessor - where TPixel : struct, IPixel - { - /// - /// Gets the used to alter the image. - /// - Matrix4x4 Matrix { get; } - } -} diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs deleted file mode 100644 index 18514236f7..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image recreating an old Kodachrome camera effect. - /// - /// The pixel format. - internal class KodachromeProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 0.6997023F, - M22 = 0.4609577F, - M33 = 0.397218F, - M41 = 0.005F, - M42 = -0.005F, - M43 = 0.005F, - M44 = 1 - }; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs deleted file mode 100644 index 9e54574df9..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// Converts the colors of the image recreating an old Polaroid effect. - /// - /// The pixel format. - internal class PolaroidProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - private static TPixel veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static TPixel lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); - private GraphicsOptions options; - - /// - /// Initializes a new instance of the class. - /// - /// The options effecting blending and composition. - public PolaroidProcessor(GraphicsOptions options) - { - this.options = options; - } - - /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 1.538F, - M12 = -0.062F, - M13 = -0.262F, - M21 = -0.022F, - M22 = 1.578F, - M23 = -0.022F, - M31 = .216F, - M32 = -.16F, - M33 = 1.5831F, - M41 = 0.02F, - M42 = -0.05F, - M43 = -0.05F, - M44 = 1 - }; - - /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) - { - new VignetteProcessor(veryDarkOrange, this.options).Apply(source, sourceRectangle); - new GlowProcessor(lightOrange, this.options) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs deleted file mode 100644 index 3adfb83114..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// An to change the saturation of an . - /// - /// The pixel format. - internal class SaturationProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The new saturation of the image. Must be between -100 and 100. - /// - /// is less than -100 or is greater than 100. - /// - public SaturationProcessor(int saturation) - { - Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation)); - float saturationFactor = saturation / 100F; - - // Stop at -1 to prevent inversion. - saturationFactor++; - - // The matrix is set up to "shear" the color space using the following set of values. - // Note that each color component has an effective luminance which contributes to the - // overall brightness of the pixel. - // See http://graficaobscura.com/matrix/index.html - float saturationComplement = 1.0f - saturationFactor; - float saturationComplementR = 0.3086f * saturationComplement; - float saturationComplementG = 0.6094f * saturationComplement; - float saturationComplementB = 0.0820f * saturationComplement; - - Matrix4x4 matrix4X4 = new Matrix4x4 - { - M11 = saturationComplementR + saturationFactor, - M12 = saturationComplementR, - M13 = saturationComplementR, - M21 = saturationComplementG, - M22 = saturationComplementG + saturationFactor, - M23 = saturationComplementG, - M31 = saturationComplementB, - M32 = saturationComplementB, - M33 = saturationComplementB + saturationFactor, - M44 = 1 - }; - - this.Matrix = matrix4X4; - } - - /// - public override Matrix4x4 Matrix { get; } - } -} diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs deleted file mode 100644 index 89be3acad5..0000000000 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - /// - /// Converts the colors of the image to their sepia equivalent. - /// The formula used matches the svg specification. - /// - /// The pixel format. - internal class SepiaProcessor : ColorMatrixProcessor - where TPixel : struct, IPixel - { - /// - public override Matrix4x4 Matrix => new Matrix4x4 - { - M11 = .393F, - M12 = .349F, - M13 = .272F, - M21 = .769F, - M22 = .686F, - M23 = .534F, - M31 = .189F, - M32 = .168F, - M33 = .131F, - M44 = 1 - }; - - /// - public override bool Compand => false; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 0a2162fb05..8056141a09 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Applies a Box blur sampler to the image. /// @@ -31,11 +28,17 @@ namespace ImageSharp.Processing.Processors /// public BoxBlurProcessor(int radius = 7) { + this.Radius = radius; this.kernelSize = (radius * 2) + 1; this.KernelX = this.CreateBoxKernel(true); this.KernelY = this.CreateBoxKernel(false); } + /// + /// Gets the Radius + /// + public int Radius { get; } + /// /// Gets the horizontal gradient operator. /// @@ -47,9 +50,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index 708b6c6fd8..cf9b7be9c8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that uses two one-dimensional matrices to perform convolution against an image. /// @@ -42,7 +40,7 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; @@ -65,10 +63,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( startY, endY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -87,7 +85,7 @@ namespace ImageSharp.Processing.Processors int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelXWidth; fx++) { @@ -95,7 +93,7 @@ namespace ImageSharp.Processing.Processors int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); - var currentColor = sourceOffsetRow[offsetX].ToVector4(); + Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); if (fy < kernelXHeight) { @@ -120,7 +118,7 @@ namespace ImageSharp.Processing.Processors float blue = MathF.Sqrt((bX * bX) + (bY * bY)); ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); } }); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index ceb985b0b7..57f434ee08 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that uses two one-dimensional matrices to perform two-pass convolution against an image. /// @@ -42,21 +41,22 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int width = source.Width; int height = source.Height; + ParallelOptions parallelOptions = configuration.ParallelOptions; using (var firstPassPixels = new PixelAccessor(width, height)) using (PixelAccessor sourcePixels = source.Lock()) { - this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds, this.KernelX); - this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY); + this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds(), this.KernelX, parallelOptions); + this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY, parallelOptions); } } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The target pixels to apply the process to. @@ -65,7 +65,13 @@ namespace ImageSharp.Processing.Processors /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel) + /// The parellel options + private void ApplyConvolution( + PixelAccessor targetPixels, + PixelAccessor sourcePixels, + Rectangle sourceRectangle, + Fast2DArray kernel, + ParallelOptions parallelOptions) { int kernelHeight = kernel.Height; int kernelWidth = kernel.Width; @@ -82,7 +88,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( startY, endY, - this.ParallelOptions, + parallelOptions, y => { Span targetRow = targetPixels.GetRowSpan(y); @@ -107,13 +113,13 @@ namespace ImageSharp.Processing.Processors offsetX = offsetX.Clamp(0, maxX); - var currentColor = row[offsetX].ToVector4(); + Vector4 currentColor = row[offsetX].ToVector4().Premultiply(); destination += kernel[fy, fx] * currentColor; } } ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(destination); + pixel.PackFromVector4(destination.UnPremultiply()); } }); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index cd2eb27004..96db9a4ce0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that uses a 2 dimensional matrix to perform convolution against an image. /// @@ -35,7 +33,7 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int kernelLength = this.KernelXY.Height; int radius = kernelLength >> 1; @@ -54,10 +52,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( startY, endY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -73,7 +71,7 @@ namespace ImageSharp.Processing.Processors int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelLength; fx++) { @@ -82,7 +80,7 @@ namespace ImageSharp.Processing.Processors offsetX = offsetX.Clamp(0, maxX); - var currentColor = sourceOffsetRow[offsetX].ToVector4(); + Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); currentColor *= this.KernelXY[fy, fx]; red += currentColor.X; @@ -92,7 +90,7 @@ namespace ImageSharp.Processing.Processors } ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); } }); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index ab0b459076..741a6e308c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that detects edges within an image using two one-dimensional matrices. /// @@ -43,17 +40,17 @@ namespace ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 367c288fca..0ffd7d48f5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that detects edges within an image using a eight two dimensional matrices. /// @@ -64,16 +62,16 @@ namespace ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; @@ -89,9 +87,9 @@ namespace ImageSharp.Processing.Processors int maxY = Math.Min(source.Height, endY); // we need a clean copy for each pass to start from - using (ImageBase cleanCopy = new Image(source)) + using (ImageFrame cleanCopy = source.Clone()) { - new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle); + new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle, configuration); if (kernels.Length == 1) { @@ -116,9 +114,9 @@ namespace ImageSharp.Processing.Processors // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) { - using (ImageBase pass = new Image(cleanCopy)) + using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle, configuration); using (PixelAccessor passPixels = pass.Lock()) using (PixelAccessor targetPixels = source.Lock()) @@ -126,7 +124,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - shiftY; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index 1400b6317a..e5c5179716 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that detects edges within an image using a single two dimensional matrix. /// @@ -36,18 +33,18 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); + new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs index c7c126794d..6208a24a46 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing +{ /// /// Provides properties and methods allowing the detection of edges within an image. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index b1361b514f..8e4cdd6b5a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Kayyali operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index af4700bb97..c0a3b35954 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Kirsch operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index 5f58d56f8c..1b5c563187 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Laplacian 3 x 3 operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index 2e365374cf..11438fe8db 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Laplacian 5 x 5 operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index de2594653d..424c01137e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Laplacian of Gaussian operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 1b2d5f50ed..0d2e9b2066 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Prewitt operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index d1b9614b62..09aa8ecb6c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Roberts Cross operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index bc687f6743..18f8cbf18c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Kirsch operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index 339b9741fc..e299d3f807 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Scharr operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index b9060ecbca..e975e4ff6c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Sobel operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index ef6ddaa6a7..c897efeed8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Applies a Gaussian blur sampler to the image. /// @@ -72,6 +69,11 @@ namespace ImageSharp.Processing.Processors this.KernelY = this.CreateGaussianKernel(false); } + /// + /// Gets the sigma + /// + public float Sigma => this.sigma; + /// /// Gets the horizontal gradient operator. /// @@ -83,9 +85,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 594dda8cab..b960e9075f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Applies a Gaussian sharpening sampler to the image. /// @@ -74,6 +71,11 @@ namespace ImageSharp.Processing.Processors this.KernelY = this.CreateGaussianKernel(false); } + /// + /// Gets the sigma + /// + public float Sigma => this.sigma; + /// /// Gets the horizontal gradient operator. /// @@ -85,9 +87,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/DelegateProcessor.cs b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs new file mode 100644 index 0000000000..f17c39681e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Allows the application of processors to images. + /// + /// The pixel format. + internal class DelegateProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly Action> action; + + /// + /// Initializes a new instance of the class. + /// + /// The action. + public DelegateProcessor(Action> action) + { + this.action = action; + } + + /// + /// Gets the action that will be applied to the image. + /// + internal Action> Action => this.action; + + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + this.action?.Invoke(source); + } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // NOP, we did all we wanted to do inside BeforeImageApply + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs deleted file mode 100644 index 0efca43b26..0000000000 --- a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// An to change the alpha component of an . - /// - /// The pixel format. - internal class AlphaProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The percentage to adjust the opacity of the image. Must be between 0 and 1. - /// - /// is less than 0 or is greater than 1. - /// - public AlphaProcessor(float percent) - { - Guard.MustBeBetweenOrEqualTo(percent, 0, 1, nameof(percent)); - this.Value = percent; - } - - /// - /// Gets the alpha value. - /// - public float Value { get; } - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - var alphaVector = new Vector4(1, 1, 1, this.Value); - - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - Span row = source.GetRowSpan(y - startY); - - for (int x = minX; x < maxX; x++) - { - ref TPixel pixel = ref row[x - startX]; - pixel.PackFromVector4(pixel.ToVector4() * alphaVector); - } - }); - } - } -} diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index 96fcf4d630..72e9b8f555 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Sets the background color of the image. /// @@ -32,13 +30,18 @@ namespace ImageSharp.Processing.Processors this.options = options; } + /// + /// Gets the Graphics options to alter how processor is applied. + /// + public GraphicsOptions GraphicsOptions => this.options; + /// /// Gets the background color value. /// public TPixel Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -77,10 +80,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span destination = source.GetRowSpan(y - startY).Slice(minX - startX, width); + Span destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one blender.Blend(destination, colors, destination, amount); diff --git a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs deleted file mode 100644 index 3fbe1742ed..0000000000 --- a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// An to change the brightness of an . - /// - /// The pixel format. - internal class BrightnessProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The new brightness of the image. Must be between -100 and 100. - /// - /// is less than -100 or is greater than 100. - /// - public BrightnessProcessor(int brightness) - { - Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness)); - this.Value = brightness; - } - - /// - /// Gets the brightness value. - /// - public int Value { get; } - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - float brightness = this.Value / 100F; - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - Span row = source.GetRowSpan(y - startY); - - for (int x = minX; x < maxX; x++) - { - ref TPixel pixel = ref row[x - startX]; - - // TODO: Check this with other formats. - Vector4 vector = pixel.ToVector4().Expand(); - Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness); - vector = new Vector4(transformed, vector.W); - - pixel.PackFromVector4(vector.Compress()); - } - }); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs deleted file mode 100644 index e40f8d5de5..0000000000 --- a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// An to change the contrast of an . - /// - /// The pixel format. - internal class ContrastProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The new contrast of the image. Must be between -100 and 100. - /// - /// is less than -100 or is greater than 100. - /// - public ContrastProcessor(int contrast) - { - Guard.MustBeBetweenOrEqualTo(contrast, -100, 100, nameof(contrast)); - this.Value = contrast; - } - - /// - /// Gets the contrast value. - /// - public int Value { get; } - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - float contrast = (100F + this.Value) / 100F; - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - var contrastVector = new Vector4(contrast, contrast, contrast, 1); - var shiftVector = new Vector4(.5F, .5F, .5F, 1); - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - Span row = source.GetRowSpan(y - startY); - - for (int x = minX; x < maxX; x++) - { - ref TPixel pixel = ref row[x - startX]; - - Vector4 vector = pixel.ToVector4().Expand(); - vector -= shiftVector; - vector *= contrastVector; - vector += shiftVector; - - pixel.PackFromVector4(vector.Compress()); - } - }); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs deleted file mode 100644 index 07a57db54a..0000000000 --- a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// An to invert the colors of an . - /// - /// The pixel format. - internal class InvertProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - Vector3 inverseVector = Vector3.One; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - Span row = source.GetRowSpan(y - startY); - - for (int x = minX; x < maxX; x++) - { - ref TPixel pixel = ref row[x - startX]; - - var vector = pixel.ToVector4(); - Vector3 vector3 = inverseVector - new Vector3(vector.X, vector.Y, vector.Z); - - pixel.PackFromVector4(new Vector4(vector3, vector.W)); - } - }); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index f484c8eec8..b22a497987 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to apply an oil painting effect to an . /// @@ -49,38 +48,34 @@ namespace ImageSharp.Processing.Processors public int BrushSize { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(this.BrushSize)); + } + int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + int radius = this.BrushSize >> 1; int levels = this.Levels; - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - using (var targetPixels = new PixelAccessor(source.Width, source.Height)) { source.CopyTo(targetPixels); Parallel.For( - minY, + startY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -100,7 +95,7 @@ namespace ImageSharp.Processing.Processors offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx <= radius; fx++) { diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index ff83117c5f..0ab21f65ac 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Common; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to pixelate the colors of an . /// @@ -29,23 +28,28 @@ namespace ImageSharp.Processing.Processors public PixelateProcessor(int size) { Guard.MustBeGreaterThan(size, 0, nameof(size)); - this.Value = size; + this.Size = size; } /// /// Gets or the pixel size. /// - public int Value { get; } + public int Size { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(this.Size)); + } + int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - int size = this.Value; - int offset = this.Value / 2; + int size = this.Size; + int offset = this.Size / 2; // Align start/end positions. int minX = Math.Max(0, startX); @@ -69,7 +73,7 @@ namespace ImageSharp.Processing.Processors Parallel.ForEach( range, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; @@ -81,7 +85,7 @@ namespace ImageSharp.Processing.Processors offsetPy--; } - Span row = source.GetRowSpan(offsetY + offsetPy); + Span row = source.GetPixelRowSpan(offsetY + offsetPy); for (int x = minX; x < maxX; x += size) { diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs new file mode 100644 index 0000000000..30fcfab4fd --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a black and white filter matrix to the image + /// + /// The pixel format. + internal class BlackWhiteProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public BlackWhiteProcessor() + : base(MatrixFilters.BlackWhiteFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs new file mode 100644 index 0000000000..b1a68a9c91 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a brightness filter matrix using the given amount. + /// + /// The pixel format. + internal class BrightnessProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + public BrightnessProcessor(float amount) + : base(MatrixFilters.CreateBrightnessFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs new file mode 100644 index 0000000000..8d9bf98579 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. + /// + /// The pixel format. + internal class AchromatomalyProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public AchromatomalyProcessor() + : base(MatrixFilters.AchromatomalyFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs new file mode 100644 index 0000000000..f19c55933d --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. + /// + /// The pixel format. + internal class AchromatopsiaProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public AchromatopsiaProcessor() + : base(MatrixFilters.AchromatopsiaFilter) + { + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs new file mode 100644 index 0000000000..20a1d4ab46 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. + /// + /// The pixel format. + internal class DeuteranomalyProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public DeuteranomalyProcessor() + : base(MatrixFilters.DeuteranomalyFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs new file mode 100644 index 0000000000..e5e0225718 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. + /// + /// The pixel format. + internal class DeuteranopiaProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public DeuteranopiaProcessor() + : base(MatrixFilters.DeuteranopiaFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs new file mode 100644 index 0000000000..b7b61d5e59 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. + /// + /// The pixel format. + internal class ProtanomalyProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public ProtanomalyProcessor() + : base(MatrixFilters.ProtanomalyFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs new file mode 100644 index 0000000000..54753f5b57 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. + /// + /// The pixel format. + internal class ProtanopiaProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public ProtanopiaProcessor() + : base(MatrixFilters.ProtanopiaFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/README.md b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md similarity index 100% rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/README.md rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs new file mode 100644 index 0000000000..57f4d4fa83 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. + /// + /// The pixel format. + internal class TritanomalyProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public TritanomalyProcessor() + : base(MatrixFilters.TritanomalyFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs new file mode 100644 index 0000000000..b03a18cf76 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. + /// + /// The pixel format. + internal class TritanopiaProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public TritanopiaProcessor() + : base(MatrixFilters.TritanopiaFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs new file mode 100644 index 0000000000..8ebeb939fb --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a contrast filter matrix using the given amount. + /// + /// The pixel format. + internal class ContrastProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + public ContrastProcessor(float amount) + : base(MatrixFilters.CreateContrastFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs new file mode 100644 index 0000000000..30fe8c6b6f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Provides methods that accept a matrix to apply freeform filters to images. + /// + /// The pixel format. + internal class FilterProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The matrix used to apply the image filter + public FilterProcessor(Matrix4x4 matrix) + { + this.Matrix = matrix; + } + + /// + /// Gets the used to apply the image filter. + /// + public Matrix4x4 Matrix { get; } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + Matrix4x4 matrix = this.Matrix; + + Parallel.For( + startY, + endY, + configuration.ParallelOptions, + y => + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + ref TPixel pixel = ref row[x]; + var vector = Vector4.Transform(pixel.ToVector4(), matrix); + pixel.PackFromVector4(vector); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs new file mode 100644 index 0000000000..7ea52dcb92 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 + /// + /// The pixel format. + internal class GrayscaleBt601Processor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The proportion of the conversion. Must be between 0 and 1. + public GrayscaleBt601Processor(float amount) + : base(MatrixFilters.CreateGrayscaleBt601Filter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs new file mode 100644 index 0000000000..2d97f65842 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 + /// + /// The pixel format. + internal class GrayscaleBt709Processor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The proportion of the conversion. Must be between 0 and 1. + public GrayscaleBt709Processor(float amount) + : base(MatrixFilters.CreateGrayscaleBt709Filter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs new file mode 100644 index 0000000000..302314db40 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a hue filter matrix using the given angle of rotation in degrees + /// + internal class HueProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The angle of rotation in degrees + public HueProcessor(float degrees) + : base(MatrixFilters.CreateHueFilter(degrees)) + { + this.Degrees = degrees; + } + + /// + /// Gets the angle of rotation in degrees + /// + public float Degrees { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs new file mode 100644 index 0000000000..e258e9d96e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a filter matrix that inverts the colors of an image + /// + /// The pixel format. + internal class InvertProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The proportion of the conversion. Must be between 0 and 1. + public InvertProcessor(float amount) + : base(MatrixFilters.CreateInvertFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs new file mode 100644 index 0000000000..6f27a04538 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image + /// + /// The pixel format. + internal class KodachromeProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public KodachromeProcessor() + : base(MatrixFilters.KodachromeFilter) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs similarity index 50% rename from src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs rename to src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index bb33e51513..5ea57fd27b 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -1,51 +1,36 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating an old Lomograph effect. /// /// The pixel format. - internal class LomographProcessor : ColorMatrixProcessor + internal class LomographProcessor : FilterProcessor where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - private GraphicsOptions options; + private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. /// /// The options effecting blending and composition. public LomographProcessor(GraphicsOptions options) + : base(MatrixFilters.LomographFilter) { this.options = options; } /// - public override Matrix4x4 Matrix => new Matrix4x4() - { - M11 = 1.5F, - M22 = 1.45F, - M33 = 1.11F, - M41 = -.1F, - M42 = .0F, - M43 = -.08F, - M44 = 1 - }; - - /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs new file mode 100644 index 0000000000..1c0d2600ea --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies an opacity filter matrix using the given amount. + /// + /// The pixel format. + internal class OpacityProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The proportion of the conversion. Must be between 0 and 1. + public OpacityProcessor(float amount) + : base(MatrixFilters.CreateOpacityFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs new file mode 100644 index 0000000000..5491db7efe --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Converts the colors of the image recreating an old Polaroid effect. + /// + /// The pixel format. + internal class PolaroidProcessor : FilterProcessor + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); + private readonly GraphicsOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The options effecting blending and composition. + public PolaroidProcessor(GraphicsOptions options) + : base(MatrixFilters.PolaroidFilter) + { + this.options = options; + } + + /// + protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + new VignetteProcessor(VeryDarkOrange, this.options).Apply(source, sourceRectangle, configuration); + new GlowProcessor(LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle, configuration); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs new file mode 100644 index 0000000000..44b3fe3ced --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a saturation filter matrix using the given amount. + /// + /// The pixel format. + internal class SaturateProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + public SaturateProcessor(float amount) + : base(MatrixFilters.CreateSaturateFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs new file mode 100644 index 0000000000..b30d0fe052 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Applies a sepia filter matrix using the given amount. + /// + /// The pixel format. + internal class SepiaProcessor : FilterProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The proportion of the conversion. Must be between 0 and 1. + public SepiaProcessor(float amount) + : base(MatrixFilters.CreateSepiaFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor.cs b/src/ImageSharp/Processing/Processors/ImageProcessor.cs new file mode 100644 index 0000000000..cab99112c1 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ImageProcessor.cs @@ -0,0 +1,118 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Allows the application of processors to images. + /// + /// The pixel format. + internal abstract class ImageProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + public void Apply(Image source, Rectangle sourceRectangle) + { + try + { + Configuration config = source.GetConfiguration(); + this.BeforeImageApply(source, sourceRectangle); + + foreach (ImageFrame sourceFrame in source.Frames) + { + this.Apply(sourceFrame, sourceRectangle, config); + } + + this.AfterImageApply(source, sourceRectangle); + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + /// Applies the processor to just a single ImageBase + /// + /// the source image + /// the target + /// The configuration. + public void Apply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + try + { + this.BeforeApply(source, sourceRectangle, configuration); + this.OnApply(source, sourceRectangle, configuration); + this.AfterApply(source, sourceRectangle, configuration); + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + protected virtual void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected abstract void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration); + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + protected virtual void AfterImageApply(Image source, Rectangle sourceRectangle) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 8a13eabca8..b02585d8fd 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that applies a radial glow effect an . /// @@ -27,14 +26,21 @@ namespace ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The color or the glow. + /// The radius of the glow. /// The options effecting blending and composition. - public GlowProcessor(TPixel color, GraphicsOptions options) + public GlowProcessor(TPixel color, ValueSize radius, GraphicsOptions options) { this.options = options; this.GlowColor = color; + this.Radius = radius; this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } + /// + /// Gets the Graphics options to alter how processor is applied. + /// + public GraphicsOptions GraphicsOptions => this.options; + /// /// Gets or sets the glow color to apply. /// @@ -43,10 +49,10 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the the radius. /// - public float Radius { get; set; } + public ValueSize Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -54,7 +60,10 @@ namespace ImageSharp.Processing.Processors int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle); - float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; + + var finalRadius = this.Radius.Calculate(source.Size()); + + float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. int minX = Math.Max(0, startX); @@ -84,7 +93,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { using (var amounts = new Buffer(width)) @@ -97,7 +106,7 @@ namespace ImageSharp.Processing.Processors amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); } - Span destination = source.GetRowSpan(offsetY).Slice(offsetX, width); + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); this.blender.Blend(destination, destination, rowColors, amounts); } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 8cb58739b0..7b592a6a4d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that applies a radial vignette effect to an . /// @@ -27,15 +26,35 @@ namespace ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The color of the vignette. + /// The x-radius. + /// The y-radius. /// The options effecting blending and composition. - public VignetteProcessor(TPixel color, GraphicsOptions options) + public VignetteProcessor(TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) { this.VignetteColor = color; + this.RadiusX = radiusX; + this.RadiusY = radiusY; + this.options = options; + this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); + } + /// + /// Initializes a new instance of the class. + /// + /// The color of the vignette. + /// The options effecting blending and composition. + public VignetteProcessor(TPixel color, GraphicsOptions options) + { + this.VignetteColor = color; this.options = options; this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } + /// + /// Gets the Graphics options to alter how processor is applied. + /// + public GraphicsOptions GraphicsOptions => this.options; + /// /// Gets or sets the vignette color to apply. /// @@ -44,15 +63,15 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the the x-radius. /// - public float RadiusX { get; set; } + public ValueSize RadiusX { get; set; } /// /// Gets or sets the the y-radius. /// - public float RadiusY { get; set; } + public ValueSize RadiusY { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -60,8 +79,11 @@ namespace ImageSharp.Processing.Processors int endX = sourceRectangle.Right; TPixel vignetteColor = this.VignetteColor; Vector2 centre = Rectangle.Center(sourceRectangle); - float rX = this.RadiusX > 0 ? MathF.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; - float rY = this.RadiusY > 0 ? MathF.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; + + var finalradiusX = this.RadiusX.Calculate(source.Size()); + var finalradiusY = this.RadiusY.Calculate(source.Size()); + float rX = finalradiusX > 0 ? MathF.Min(finalradiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; + float rY = finalradiusY > 0 ? MathF.Min(finalradiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); // Align start/end positions. @@ -92,7 +114,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { using (var amounts = new Buffer(width)) @@ -105,7 +127,7 @@ namespace ImageSharp.Processing.Processors amounts[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1); } - Span destination = source.GetRowSpan(offsetY).Slice(offsetX, width); + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); this.blender.Blend(destination, destination, rowColors, amounts); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs new file mode 100644 index 0000000000..8595e86922 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -0,0 +1,245 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Provides the base methods to perform affine transforms on an image. + /// + /// The pixel format. + internal class AffineTransformProcessor : InterpolatedTransformProcessorBase + where TPixel : struct, IPixel + { + private Size targetDimensions; + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + public AffineTransformProcessor(Matrix3x2 matrix) + : this(matrix, KnownResamplers.Bicubic) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + /// The sampler to perform the transform operation. + public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler) + : this(matrix, sampler, Size.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + /// The sampler to perform the transform operation. + /// The target dimensions to constrain the transformed image to. + public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) + : base(sampler) + { + // Transforms are inverted else the output is the opposite of the expected. + Matrix3x2.Invert(matrix, out matrix); + this.TransformMatrix = matrix; + this.targetDimensions = targetDimensions; + } + + /// + /// Gets the matrix used to supply the affine transform + /// + public Matrix3x2 TransformMatrix { get; } + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + if (this.targetDimensions == Size.Empty) + { + // TODO: CreateDestination() should not modify the processors state! (kinda CQRS) + this.targetDimensions = this.GetTransformedDimensions(sourceRectangle.Size, this.TransformMatrix); + } + + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(this.targetDimensions, x.MetaData.Clone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); + } + + /// + protected override void OnApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) + { + int height = this.targetDimensions.Height; + int width = this.targetDimensions.Width; + + Rectangle sourceBounds = source.Bounds(); + var targetBounds = new Rectangle(0, 0, width, height); + + // Since could potentially be resizing the canvas we might need to re-calculate the matrix + Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); + + if (this.Sampler is NearestNeighborResampler) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + var point = Point.Transform(new Point(x, y), matrix); + if (sourceBounds.Contains(point.X, point.Y)) + { + destRow[x] = source[point.X, point.Y]; + } + } + }); + + return; + } + + int maxSourceX = source.Width - 1; + int maxSourceY = source.Height - 1; + (float radius, float scale, float ratio) xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width); + (float radius, float scale, float ratio) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height); + float xScale = xRadiusScale.scale; + float yScale = yRadiusScale.scale; + var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius); + IResampler sampler = this.Sampler; + var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY); + int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); + int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); + + using (var yBuffer = new Buffer2D(yLength, height)) + using (var xBuffer = new Buffer2D(xLength, height)) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span destRow = destination.GetPixelRowSpan(y); + Span ySpan = yBuffer.GetRowSpan(y); + Span xSpan = xBuffer.GetRowSpan(y); + + for (int x = 0; x < width; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var point = Vector2.Transform(new Vector2(x, y), matrix); + + // Clamp sampling pixel radial extents to the source image edges + Vector2 maxXY = point + radius; + Vector2 minXY = point - radius; + + // max, maxY, minX, minY + var extents = new Vector4( + MathF.Floor(maxXY.X + .5F), + MathF.Floor(maxXY.Y + .5F), + MathF.Ceiling(minXY.X - .5F), + MathF.Ceiling(minXY.Y - .5F)); + + int right = (int)extents.X; + int bottom = (int)extents.Y; + int left = (int)extents.Z; + int top = (int)extents.W; + + extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); + + int maxX = (int)extents.X; + int maxY = (int)extents.Y; + int minX = (int)extents.Z; + int minY = (int)extents.W; + + if (minX == maxX || minY == maxY) + { + continue; + } + + // It appears these have to be calculated on-the-fly. + // Precalulating transformed weights would require prior knowledge of every transformed pixel location + // since they can be at sub-pixel positions on both axis. + // I've optimized where I can but am always open to suggestions. + if (yScale > 1 && xScale > 1) + { + CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan); + CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan); + } + else + { + CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan); + CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan); + } + + // Now multiply the results against the offsets + Vector4 sum = Vector4.Zero; + for (int yy = 0, j = minY; j <= maxY; j++, yy++) + { + float yWeight = ySpan[yy]; + + for (int xx = 0, i = minX; i <= maxX; i++, xx++) + { + float xWeight = xSpan[xx]; + var vector = source[i, j].ToVector4(); + + // Values are first premultiplied to prevent darkening of edge pixels + Vector4 mupltiplied = vector.Premultiply(); + sum += mupltiplied * xWeight * yWeight; + } + } + + ref TPixel dest = ref destRow[x]; + + // Reverse the premultiplication + dest.PackFromVector4(sum.UnPremultiply()); + } + }); + } + } + + /// + /// Gets a transform matrix adjusted for final processing based upon the target image bounds. + /// + /// The source image bounds. + /// The destination image bounds. + /// + /// The . + /// + protected virtual Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) + { + return this.TransformMatrix; + } + + /// + /// Gets the bounding relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// The + protected virtual Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) + { + return sourceDimensions; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs new file mode 100644 index 0000000000..c39311bc33 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -0,0 +1,102 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. + /// + /// The pixel format. + internal class AutoOrientProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + Orientation orientation = GetExifOrientation(source); + + switch (orientation) + { + case Orientation.TopRight: + new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); + break; + + case Orientation.BottomRight: + new RotateProcessor((int)RotateType.Rotate180).Apply(source, sourceRectangle); + break; + + case Orientation.BottomLeft: + new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); + break; + + case Orientation.LeftTop: + new RotateProcessor((int)RotateType.Rotate90).Apply(source, sourceRectangle); + new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); + break; + + case Orientation.RightTop: + new RotateProcessor((int)RotateType.Rotate90).Apply(source, sourceRectangle); + break; + + case Orientation.RightBottom: + new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateType.Rotate270).Apply(source, sourceRectangle); + break; + + case Orientation.LeftBottom: + new RotateProcessor((int)RotateType.Rotate270).Apply(source, sourceRectangle); + break; + + case Orientation.Unknown: + case Orientation.TopLeft: + default: + break; + } + } + + /// + protected override void OnApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + { + // all processing happens at the image level within BeforeImageApply(); + } + + /// + /// Returns the current EXIF orientation + /// + /// The image to auto rotate. + /// The + private static Orientation GetExifOrientation(Image source) + { + if (source.MetaData.ExifProfile == null) + { + return Orientation.Unknown; + } + + ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); + if (value == null) + { + return Orientation.Unknown; + } + + Orientation orientation; + if (value.DataType == ExifDataType.Short) + { + orientation = (Orientation)value.Value; + } + else + { + orientation = (Orientation)Convert.ToUInt16(value.Value); + source.MetaData.ExifProfile.RemoveValue(ExifTag.Orientation); + } + + source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); + + return orientation; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs new file mode 100644 index 0000000000..34a0866615 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// A base class that provides methods to allow the automatic centering of affine transforms + /// + /// The pixel format. + internal abstract class CenteredAffineTransformProcessor : AffineTransformProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + /// The sampler to perform the transform operation. + protected CenteredAffineTransformProcessor(Matrix3x2 matrix, IResampler sampler) + : base(matrix, sampler) + { + } + + /// + protected override Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) + { + var translationToTargetCenter = Matrix3x2.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F); + var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F); + return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter; + } + + /// + protected override Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) + { + var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); + + if (!Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix)) + { + // TODO: Shouldn't we throw an exception instead? + return sourceDimensions; + } + + return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix).Size; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs new file mode 100644 index 0000000000..dc2dd28ab1 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// A base class that provides methods to allow the automatic centering of non-affine transforms + /// + /// The pixel format. + internal abstract class CenteredProjectiveTransformProcessor : ProjectiveTransformProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + /// The sampler to perform the transform operation. + protected CenteredProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler) + : base(matrix, sampler) + { + } + + /// + protected override Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) + { + var translationToTargetCenter = Matrix4x4.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F, 0); + var translateToSourceCenter = Matrix4x4.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F, 0); + return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter; + } + + /// + protected override Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix4x4 matrix) + { + return Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 sizeMatrix) + ? TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix) + : sourceRectangle; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 37f8867755..00547d0147 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods to allow the cropping of an image. /// @@ -33,7 +32,7 @@ namespace ImageSharp.Processing.Processors public Rectangle CropRectangle { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.CropRectangle == sourceRectangle) { @@ -50,10 +49,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(minX, y); + Span sourceRow = source.GetPixelRowSpan(y).Slice(minX); Span targetRow = targetPixels.GetRowSpan(y - minY); SpanHelper.Copy(sourceRow, targetRow, maxX - minX); }); @@ -61,5 +60,9 @@ namespace ImageSharp.Processing.Processors source.SwapPixelsBuffers(targetPixels); } } + + /// + protected override void AfterImageApply(Image source, Rectangle sourceRectangle) + => TransformHelpers.UpdateDimensionalMetData(source); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 16f74f218b..d2a08daba6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods to allow the cropping of an image to preserve areas of highest /// entropy. @@ -28,24 +25,24 @@ namespace ImageSharp.Processing.Processors public EntropyCropProcessor(float threshold) { Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); - this.Value = threshold; + this.Threshold = threshold; } /// /// Gets the threshold value. /// - public float Value { get; } + public float Threshold { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - using (ImageBase temp = new Image(source)) + using (ImageFrame temp = source.Clone()) { // Detect the edges. - new SobelProcessor().Apply(temp, sourceRectangle); + new SobelProcessor().Apply(temp, sourceRectangle, configuration); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); + new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); @@ -55,7 +52,7 @@ namespace ImageSharp.Processing.Processors return; } - new CropProcessor(rectangle).Apply(source, sourceRectangle); + new CropProcessor(rectangle).Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index cba60f928e..de60177f2f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the flipping of an image around its center point. /// @@ -34,16 +32,16 @@ namespace ImageSharp.Processing.Processors public FlipType FlipType { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { switch (this.FlipType) { // No default needed as we have already set the pixels. case FlipType.Vertical: - this.FlipX(source); + this.FlipX(source, configuration); break; case FlipType.Horizontal: - this.FlipY(source); + this.FlipY(source, configuration); break; } } @@ -53,7 +51,8 @@ namespace ImageSharp.Processing.Processors /// at half the height of the image. /// /// The source image to apply the process to. - private void FlipX(ImageBase source) + /// The configuration. + private void FlipX(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -64,12 +63,12 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, halfHeight, - this.ParallelOptions, + configuration.ParallelOptions, y => { int newY = height - y - 1; - Span sourceRow = source.GetRowSpan(y); - Span altSourceRow = source.GetRowSpan(newY); + Span sourceRow = source.GetPixelRowSpan(y); + Span altSourceRow = source.GetPixelRowSpan(newY); Span targetRow = targetPixels.GetRowSpan(y); Span altTargetRow = targetPixels.GetRowSpan(newY); @@ -86,7 +85,8 @@ namespace ImageSharp.Processing.Processors /// at half of the width of the image. /// /// The source image to apply the process to. - private void FlipY(ImageBase source) + /// The configuration. + private void FlipY(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -97,10 +97,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = 0; x < halfWidth; x++) diff --git a/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs new file mode 100644 index 0000000000..27f9a1ace6 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs @@ -0,0 +1,117 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// The base class for performing interpolated affine and non-affine transforms. + /// + /// The pixel format. + internal abstract class InterpolatedTransformProcessorBase : TransformProcessorBase + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The sampler to perform the transform operation. + protected InterpolatedTransformProcessorBase(IResampler sampler) + { + this.Sampler = sampler; + } + + /// + /// Gets the sampler to perform interpolation of the transform operation. + /// + public IResampler Sampler { get; } + + /// + /// Calculated the weights for the given point. + /// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered. + /// Additionally the weights are nomalized. + /// + /// The minimum sampling offset + /// The maximum sampling offset + /// The minimum source bounds + /// The maximum source bounds + /// The transformed point dimension + /// The sampler + /// The transformed image scale relative to the source + /// The collection of weights + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected static void CalculateWeightsDown(int min, int max, int sourceMin, int sourceMax, float point, IResampler sampler, float scale, Span weights) + { + float sum = 0; + ref float weightsBaseRef = ref weights[0]; + + // Downsampling weights requires more edge sampling plus normalization of the weights + for (int x = 0, i = min; i <= max; i++, x++) + { + int index = i; + if (index < sourceMin) + { + index = sourceMin; + } + + if (index > sourceMax) + { + index = sourceMax; + } + + float weight = sampler.GetValue((index - point) / scale); + sum += weight; + Unsafe.Add(ref weightsBaseRef, x) = weight; + } + + if (sum > 0) + { + for (int i = 0; i < weights.Length; i++) + { + ref float wRef = ref Unsafe.Add(ref weightsBaseRef, i); + wRef = wRef / sum; + } + } + } + + /// + /// Calculated the weights for the given point. + /// + /// The minimum source bounds + /// The maximum source bounds + /// The transformed point dimension + /// The sampler + /// The collection of weights + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected static void CalculateWeightsScaleUp(int sourceMin, int sourceMax, float point, IResampler sampler, Span weights) + { + ref float weightsBaseRef = ref weights[0]; + for (int x = 0, i = sourceMin; i <= sourceMax; i++, x++) + { + float weight = sampler.GetValue(i - point); + Unsafe.Add(ref weightsBaseRef, x) = weight; + } + } + + /// + /// Calculates the sampling radius for the current sampler + /// + /// The source dimension size + /// The destination dimension size + /// The radius, and scaling factor + protected (float radius, float scale, float ratio) GetSamplingRadius(int sourceSize, int destinationSize) + { + float ratio = (float)sourceSize / destinationSize; + float scale = ratio; + + if (scale < 1F) + { + scale = 1F; + } + + return (MathF.Ceiling(scale * this.Sampler.Radius), scale, ratio); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs deleted file mode 100644 index d1a35659b3..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// Provides methods to transform an image using a . - /// - /// The pixel format. - internal abstract class Matrix3x2Processor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Gets the rectangle designating the target canvas. - /// - protected Rectangle CanvasRectangle { get; private set; } - - /// - /// Creates a new target canvas to contain the results of the matrix transform. - /// - /// The source rectangle. - /// The processing matrix. - protected void CreateNewCanvas(Rectangle sourceRectangle, Matrix3x2 processMatrix) - { - Matrix3x2 sizeMatrix; - this.CanvasRectangle = Matrix3x2.Invert(processMatrix, out sizeMatrix) - ? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix) - : sourceRectangle; - } - - /// - /// Gets a transform matrix adjusted to center upon the target image bounds. - /// - /// The source image. - /// The transform matrix. - /// - /// The . - /// - protected Matrix3x2 GetCenteredMatrix(ImageBase source, Matrix3x2 matrix) - { - var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F); - var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); - return (translationToTargetCenter * matrix) * translateToSourceCenter; - } - } -} diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs new file mode 100644 index 0000000000..7e547727e6 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -0,0 +1,240 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Provides the base methods to perform non-affine transforms on an image. + /// TODO: Doesn't work yet! Implement tests + Finish implementation + Document Matrix4x4 behavior + /// + /// The pixel format. + internal class ProjectiveTransformProcessor : InterpolatedTransformProcessorBase + where TPixel : struct, IPixel + { + // TODO: We should use a Size instead! (See AffineTransformProcessor) + private Rectangle targetRectangle; + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + public ProjectiveTransformProcessor(Matrix4x4 matrix) + : this(matrix, KnownResamplers.Bicubic) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + /// The sampler to perform the transform operation. + public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler) + : this(matrix, sampler, Rectangle.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The transform matrix + /// The sampler to perform the transform operation. + /// The rectangle to constrain the transformed image to. + public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Rectangle rectangle) + : base(sampler) + { + // Transforms are inverted else the output is the opposite of the expected. + Matrix4x4.Invert(matrix, out matrix); + this.TransformMatrix = matrix; + this.targetRectangle = rectangle; + } + + /// + /// Gets the matrix used to supply the non-affine transform + /// + public Matrix4x4 TransformMatrix { get; } + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + if (this.targetRectangle == Rectangle.Empty) + { + this.targetRectangle = this.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix); + } + + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(this.targetRectangle.Width, this.targetRectangle.Height, x.MetaData.Clone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); + } + + /// + protected override void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + int height = this.targetRectangle.Height; + int width = this.targetRectangle.Width; + Rectangle sourceBounds = source.Bounds(); + + // Since could potentially be resizing the canvas we might need to re-calculate the matrix + Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, this.targetRectangle); + + if (this.Sampler is NearestNeighborResampler) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + var point = Point.Round(Vector2.Transform(new Vector2(x, y), matrix)); + if (sourceBounds.Contains(point.X, point.Y)) + { + destRow[x] = source[point.X, point.Y]; + } + } + }); + + return; + } + + int maxSourceX = source.Width - 1; + int maxSourceY = source.Height - 1; + (float radius, float scale, float ratio) xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width); + (float radius, float scale, float ratio) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height); + float xScale = xRadiusScale.scale; + float yScale = yRadiusScale.scale; + var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius); + IResampler sampler = this.Sampler; + var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY); + int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); + int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); + + using (var yBuffer = new Buffer2D(yLength, height)) + using (var xBuffer = new Buffer2D(xLength, height)) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span destRow = destination.GetPixelRowSpan(y); + Span ySpan = yBuffer.GetRowSpan(y); + Span xSpan = xBuffer.GetRowSpan(y); + + for (int x = 0; x < width; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var point = Vector2.Transform(new Vector2(x, y), matrix); + + // Clamp sampling pixel radial extents to the source image edges + Vector2 maxXY = point + radius; + Vector2 minXY = point - radius; + + // max, maxY, minX, minY + var extents = new Vector4( + MathF.Floor(maxXY.X + .5F), + MathF.Floor(maxXY.Y + .5F), + MathF.Ceiling(minXY.X - .5F), + MathF.Ceiling(minXY.Y - .5F)); + + int right = (int)extents.X; + int bottom = (int)extents.Y; + int left = (int)extents.Z; + int top = (int)extents.W; + + extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); + + int maxX = (int)extents.X; + int maxY = (int)extents.Y; + int minX = (int)extents.Z; + int minY = (int)extents.W; + + if (minX == maxX || minY == maxY) + { + continue; + } + + // It appears these have to be calculated on-the-fly. + // Precalulating transformed weights would require prior knowledge of every transformed pixel location + // since they can be at sub-pixel positions on both axis. + // I've optimized where I can but am always open to suggestions. + if (yScale > 1 && xScale > 1) + { + CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan); + CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan); + } + else + { + CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan); + CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan); + } + + // Now multiply the results against the offsets + Vector4 sum = Vector4.Zero; + for (int yy = 0, j = minY; j <= maxY; j++, yy++) + { + float yWeight = ySpan[yy]; + + for (int xx = 0, i = minX; i <= maxX; i++, xx++) + { + float xWeight = xSpan[xx]; + var vector = source[i, j].ToVector4(); + + // Values are first premultiplied to prevent darkening of edge pixels + Vector4 mupltiplied = vector.Premultiply(); + sum += mupltiplied * xWeight * yWeight; + } + } + + ref TPixel dest = ref destRow[x]; + + // Reverse the premultiplication + dest.PackFromVector4(sum.UnPremultiply()); + } + }); + } + } + + /// + /// Gets a transform matrix adjusted for final processing based upon the target image bounds. + /// + /// The source image bounds. + /// The destination image bounds. + /// + /// The . + /// + protected virtual Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) + { + return this.TransformMatrix; + } + + /// + /// Gets the bounding relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// The + protected virtual Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix4x4 matrix) + { + return sourceRectangle; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs deleted file mode 100644 index 8aef87ec85..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ /dev/null @@ -1,202 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - - /// - /// Conains the definition of and . - /// - internal abstract partial class ResamplingWeightedProcessor - { - /// - /// Points to a collection of of weights allocated in . - /// - internal struct WeightsWindow - { - /// - /// The local left index position - /// - public int Left; - - /// - /// The length of the weights window - /// - public int Length; - - /// - /// The index in the destination buffer - /// - private readonly int flatStartIndex; - - /// - /// The buffer containing the weights values. - /// - private readonly Buffer buffer; - - /// - /// Initializes a new instance of the struct. - /// - /// The destination index in the buffer - /// The local left index - /// The span - /// The length of the window - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal WeightsWindow(int index, int left, Buffer2D buffer, int length) - { - this.flatStartIndex = (index * buffer.Width) + left; - this.Left = left; - this.buffer = buffer; - this.Length = length; - } - - /// - /// Gets a reference to the first item of the window. - /// - /// The reference to the first item of the window - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref float GetStartReference() - { - return ref this.buffer[this.flatStartIndex]; - } - - /// - /// Gets the span representing the portion of the that this window covers - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length); - - /// - /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. - /// - /// The input span of vectors - /// The source row position. - /// The weighted sum - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX) - { - ref float horizontalValues = ref this.GetStartReference(); - int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); - - // Destination color components - Vector4 result = Vector4.Zero; - - for (int i = 0; i < this.Length; i++) - { - float weight = Unsafe.Add(ref horizontalValues, i); - Vector4 v = Unsafe.Add(ref vecPtr, i); - result += v * weight; - } - - return result; - } - - /// - /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. - /// Applies to all input vectors. - /// - /// The input span of vectors - /// The source row position. - /// The weighted sum - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX) - { - ref float horizontalValues = ref this.GetStartReference(); - int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); - - // Destination color components - Vector4 result = Vector4.Zero; - - for (int i = 0; i < this.Length; i++) - { - float weight = Unsafe.Add(ref horizontalValues, i); - Vector4 v = Unsafe.Add(ref vecPtr, i); - result += v.Expand() * weight; - } - - return result; - } - - /// - /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x', - /// weighted by weight values, pointed by this instance. - /// - /// The buffer of input vectors in row first order - /// The row position - /// The source column position. - /// The weighted sum - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY) - { - ref float verticalValues = ref this.GetStartReference(); - int left = this.Left; - - // Destination color components - Vector4 result = Vector4.Zero; - - for (int i = 0; i < this.Length; i++) - { - float yw = Unsafe.Add(ref verticalValues, i); - int index = left + i + sourceY; - result += firstPassPixels[x, index] * yw; - } - - return result; - } - } - - /// - /// Holds the values in an optimized contigous memory region. - /// - internal class WeightsBuffer : IDisposable - { - private readonly Buffer2D dataBuffer; - - /// - /// Initializes a new instance of the class. - /// - /// The size of the source window - /// The size of the destination window - public WeightsBuffer(int sourceSize, int destinationSize) - { - this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); - this.Weights = new WeightsWindow[destinationSize]; - } - - /// - /// Gets the calculated values. - /// - public WeightsWindow[] Weights { get; } - - /// - /// Disposes instance releasing it's backing buffer. - /// - public void Dispose() - { - this.dataBuffer.Dispose(); - } - - /// - /// Slices a weights value at the given positions. - /// - /// The index in destination buffer - /// The local left index value - /// The local right index value - /// The weights - public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) - { - return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 0186a8fa8e..b9cb58707c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -1,22 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the resizing of images using various algorithms. /// Adapted from /// /// The pixel format. - internal abstract partial class ResamplingWeightedProcessor : ImageProcessor + internal abstract class ResamplingWeightedProcessor : TransformProcessorBase where TPixel : struct, IPixel { /// @@ -46,19 +43,19 @@ namespace ImageSharp.Processing.Processors public IResampler Sampler { get; } /// - /// Gets the width. + /// Gets or sets the width. /// - public int Width { get; } + public int Width { get; protected set; } /// - /// Gets the height. + /// Gets or sets the height. /// - public int Height { get; } + public int Height { get; protected set; } /// - /// Gets the resize rectangle. + /// Gets or sets the resize rectangle. /// - public Rectangle ResizeRectangle { get; } + public Rectangle ResizeRectangle { get; protected set; } /// /// Gets or sets the horizontal weights. @@ -140,7 +137,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { if (!(this.Sampler is NearestNeighborResampler)) { @@ -155,9 +152,9 @@ namespace ImageSharp.Processing.Processors } /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - base.AfterApply(source, sourceRectangle); + base.AfterApply(source, destination, sourceRectangle, configuration); this.HorizontalWeights?.Dispose(); this.HorizontalWeights = null; this.VerticalWeights?.Dispose(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index be1680cf1c..0d8d0d9117 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -1,18 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors { - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - /// /// Provides methods that allow the resizing of images using various algorithms. /// @@ -45,12 +46,30 @@ namespace ImageSharp.Processing.Processors { } + /// + /// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing. + /// + public bool Compand { get; set; } + /// - protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); + } + + /// + protected override void OnApply(ImageFrame source, ImageFrame cloned, Rectangle sourceRectangle, Configuration configuration) { // Jump out, we'll deal with that later. - if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) + if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle) { + // the cloned will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(cloned.GetPixelSpan()); return; } @@ -74,113 +93,122 @@ namespace ImageSharp.Processing.Processors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; - using (var targetPixels = new PixelAccessor(width, height)) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + // Y coordinates of source points + Span sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span targetRow = cloned.GetPixelRowSpan(y); + + for (int x = minX; x < maxX; x++) { - // Y coordinates of source points - Span sourceRow = source.GetRowSpan((int)(((y - startY) * heightFactor) + sourceY)); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = minX; x < maxX; x++) - { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; - } - }); + // X coordinates of source points + targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; + } + }); - // Break out now. - source.SwapPixelsBuffers(targetPixels); - return; - } + return; } // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (var targetPixels = new PixelAccessor(width, height)) + using (var firstPassPixels = new Buffer2D(width, source.Height)) { - using (var firstPassPixels = new Buffer2D(width, source.Height)) - { - firstPassPixels.Clear(); - - Parallel.For( - 0, - sourceRectangle.Bottom, - this.ParallelOptions, - y => + firstPassPixels.Clear(); + + Parallel.For( + 0, + sourceRectangle.Bottom, + configuration.ParallelOptions, + y => + { + // TODO: Without Parallel.For() this buffer object could be reused: + using (var tempRowBuffer = new Buffer(source.Width)) { - // TODO: Without Parallel.For() this buffer object could be reused: - using (var tempRowBuffer = new Buffer(source.Width)) - { - Span firstPassRow = firstPassPixels.GetRowSpan(y); - Span sourceRow = source.GetRowSpan(y); - PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length); + Span firstPassRow = firstPassPixels.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length); - if (this.Compand) + if (this.Compand) + { + for (int x = minX; x < maxX; x++) { - for (int x = minX; x < maxX; x++) - { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); - } + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); } - else + } + else + { + for (int x = minX; x < maxX; x++) { - for (int x = minX; x < maxX; x++) - { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); - } + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); } } - }); - - // Now process the rows. - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - // Ensure offsets are normalised for cropping and padding. - WeightsWindow window = this.VerticalWeights.Weights[y - startY]; - Span targetRow = targetPixels.GetRowSpan(y); + } + }); - if (this.Compand) + // Now process the rows. + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + // Ensure offsets are normalised for cropping and padding. + WeightsWindow window = this.VerticalWeights.Weights[y - startY]; + Span targetRow = cloned.GetPixelRowSpan(y); + + if (this.Compand) + { + for (int x = 0; x < width; x++) { - for (int x = 0; x < width; x++) - { - // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); - destination = destination.Compress(); + // Destination color components + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); + destination = destination.Compress(); - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(destination); - } + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4(destination); } - else + } + else + { + for (int x = 0; x < width; x++) { - for (int x = 0; x < width; x++) - { - // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); + // Destination color components + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(destination); - } + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4(destination); } - }); - } + } + }); + } + } - source.SwapPixelsBuffers(targetPixels); + /// + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + ExifProfile profile = destination.MetaData.ExifProfile; + if (profile == null) + { + return; + } + + if (profile.GetValue(ExifTag.PixelXDimension) != null) + { + profile.SetValue(ExifTag.PixelXDimension, destination.Width); + } + + if (profile.GetValue(ExifTag.PixelYDimension) != null) + { + profile.SetValue(ExifTag.PixelYDimension, destination.Height); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index e6b1d180f1..b93c869152 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -1,119 +1,132 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the rotating of images. /// /// The pixel format. - internal class RotateProcessor : Matrix3x2Processor + internal class RotateProcessor : CenteredAffineTransformProcessor where TPixel : struct, IPixel { /// - /// The transform matrix to apply. + /// Initializes a new instance of the class. /// - private Matrix3x2 processMatrix; + /// The angle of rotation in degrees. + public RotateProcessor(float degrees) + : this(degrees, KnownResamplers.Bicubic) + { + } /// - /// Gets or sets the angle of processMatrix in degrees. + /// Initializes a new instance of the class. /// - public float Angle { get; set; } + /// The angle of rotation in degrees. + /// The sampler to perform the rotating operation. + public RotateProcessor(float degrees, IResampler sampler) + : base(Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty), sampler) + { + this.Degrees = degrees; + } /// - /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. + /// Gets the angle of rotation in degrees. /// - public bool Expand { get; set; } = true; + public float Degrees { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - if (this.OptimizedApply(source)) + if (this.OptimizedApply(source, destination, configuration)) { return; } - int height = this.CanvasRectangle.Height; - int width = this.CanvasRectangle.Width; - Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); + base.OnApply(source, destination, sourceRectangle, configuration); + } - using (var targetPixels = new PixelAccessor(width, height)) + /// + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + ExifProfile profile = destination.MetaData.ExifProfile; + if (profile == null) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - Span targetRow = targetPixels.GetRowSpan(y); + return; + } - for (int x = 0; x < width; x++) - { - var transformedPoint = Point.Rotate(new Point(x, y), matrix); + if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon) + { + // No need to do anything so return. + return; + } - if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) - { - targetRow[x] = source[transformedPoint.X, transformedPoint.Y]; - } - } - }); + profile.RemoveValue(ExifTag.Orientation); - source.SwapPixelsBuffers(targetPixels); - } + base.AfterImageApply(source, destination, sourceRectangle); } - /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + /// + /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range + /// + /// The angle of rotation in degrees. + /// The + private static float WrapDegrees(float degrees) { - if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon) - { - return; - } + degrees = degrees % 360; - this.processMatrix = Matrix3x2Extensions.CreateRotation(-this.Angle, new Point(0, 0)); - if (this.Expand) + while (degrees < 0) { - this.CreateNewCanvas(sourceRectangle, this.processMatrix); + degrees += 360; } + + return degrees; } /// /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// /// The source image. - /// The - private bool OptimizedApply(ImageBase source) + /// The destination image. + /// The configuration. + /// + /// The + /// + private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) { - if (MathF.Abs(this.Angle) < Constants.Epsilon) + // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. + float degrees = WrapDegrees(this.Degrees); + + if (MathF.Abs(degrees) < Constants.Epsilon) { - // No need to do anything so return. + // The destination will be blank here so copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return true; } - if (MathF.Abs(this.Angle - 90) < Constants.Epsilon) + if (MathF.Abs(degrees - 90) < Constants.Epsilon) { - this.Rotate90(source); + this.Rotate90(source, destination, configuration); return true; } - if (MathF.Abs(this.Angle - 180) < Constants.Epsilon) + if (MathF.Abs(degrees - 180) < Constants.Epsilon) { - this.Rotate180(source); + this.Rotate180(source, destination, configuration); return true; } - if (MathF.Abs(this.Angle - 270) < Constants.Epsilon) + if (MathF.Abs(degrees - 270) < Constants.Epsilon) { - this.Rotate270(source); + this.Rotate270(source, destination, configuration); return true; } @@ -124,92 +137,90 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 270 degrees clockwise at the centre point. /// /// The source image. - private void Rotate270(ImageBase source) + /// The destination image. + /// The configuration. + private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); - using (var targetPixels = new PixelAccessor(height, width)) - { - using (PixelAccessor sourcePixels = source.Lock()) + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => + Span sourceRow = source.GetPixelRowSpan(y); + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + + if (destinationBounds.Contains(newX, newY)) { - for (int x = 0; x < width; x++) - { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - targetPixels[newX, newY] = sourcePixels[x, y]; - } - }); - } - - source.SwapPixelsBuffers(targetPixels); - } + destination[newX, newY] = sourceRow[x]; + } + } + }); } /// /// Rotates the image 180 degrees clockwise at the centre point. /// /// The source image. - private void Rotate180(ImageBase source) + /// The destination image. + /// The configuration. + private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; - using (var targetPixels = new PixelAccessor(width, height)) - { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - Span sourceRow = source.GetRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(height - y - 1); - - for (int x = 0; x < width; x++) - { - targetRow[width - x - 1] = sourceRow[x]; - } - }); + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = destination.GetPixelRowSpan(height - y - 1); - source.SwapPixelsBuffers(targetPixels); - } + for (int x = 0; x < width; x++) + { + targetRow[width - x - 1] = sourceRow[x]; + } + }); } /// /// Rotates the image 90 degrees clockwise at the centre point. /// /// The source image. - private void Rotate90(ImageBase source) + /// The destination image. + /// The configuration. + private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); - using (var targetPixels = new PixelAccessor(height, width)) - { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => + { + Span sourceRow = source.GetPixelRowSpan(y); + int newX = height - y - 1; + for (int x = 0; x < width; x++) { - Span sourceRow = source.GetRowSpan(y); - int newX = height - y - 1; - for (int x = 0; x < width; x++) + if (destinationBounds.Contains(newX, x)) { - targetPixels[newX, x] = sourceRow[x]; + destination[newX, x] = sourceRow[x]; } - }); - - source.SwapPixelsBuffers(targetPixels); - } + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 9766caa696..8c47b35274 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -1,84 +1,49 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the skewing of images. /// /// The pixel format. - internal class SkewProcessor : Matrix3x2Processor + internal class SkewProcessor : CenteredAffineTransformProcessor where TPixel : struct, IPixel { /// - /// The transform matrix to apply. + /// Initializes a new instance of the class. /// - private Matrix3x2 processMatrix; + /// The angle in degrees to perform the skew along the x-axis. + /// The angle in degrees to perform the skew along the y-axis. + public SkewProcessor(float degreesX, float degreesY) + : this(degreesX, degreesY, KnownResamplers.Bicubic) + { + } /// - /// Gets or sets the angle of rotation along the x-axis in degrees. + /// Initializes a new instance of the class. /// - public float AngleX { get; set; } + /// The angle in degrees to perform the skew along the x-axis. + /// The angle in degrees to perform the skew along the y-axis. + /// The sampler to perform the skew operation. + public SkewProcessor(float degreesX, float degreesY, IResampler sampler) + : base(Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty), sampler) + { + this.DegreesX = degreesX; + this.DegreesY = degreesY; + } /// - /// Gets or sets the angle of rotation along the y-axis in degrees. + /// Gets the angle of rotation along the x-axis in degrees. /// - public float AngleY { get; set; } + public float DegreesX { get; } /// - /// Gets or sets a value indicating whether to expand the canvas to fit the skewed image. + /// Gets the angle of rotation along the y-axis in degrees. /// - public bool Expand { get; set; } = true; - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - int height = this.CanvasRectangle.Height; - int width = this.CanvasRectangle.Width; - Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - - using (var targetPixels = new PixelAccessor(width, height)) - { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = 0; x < width; x++) - { - var transformedPoint = Point.Skew(new Point(x, y), matrix); - - if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) - { - targetRow[x] = source[transformedPoint.X, transformedPoint.Y]; - } - } - }); - - source.SwapPixelsBuffers(targetPixels); - } - } - - /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) - { - this.processMatrix = Matrix3x2Extensions.CreateSkew(-this.AngleX, -this.AngleY, new Point(0, 0)); - if (this.Expand) - { - this.CreateNewCanvas(sourceRectangle, this.processMatrix); - } - } + public float DegreesY { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs new file mode 100644 index 0000000000..7403a400e7 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// The base class for all transform processors. Any processor that changes the dimensions of the image should inherit from this. + /// + /// The pixel format. + internal abstract class TransformProcessorBase : CloningImageProcessor + where TPixel : struct, IPixel + { + /// + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + => TransformHelpers.UpdateDimensionalMetData(destination); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs new file mode 100644 index 0000000000..0e91087f90 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Holds the values in an optimized contigous memory region. + /// + internal class WeightsBuffer : IDisposable + { + private readonly Buffer2D dataBuffer; + + /// + /// Initializes a new instance of the class. + /// + /// The size of the source window + /// The size of the destination window + public WeightsBuffer(int sourceSize, int destinationSize) + { + this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); + this.Weights = new WeightsWindow[destinationSize]; + } + + /// + /// Gets the calculated values. + /// + public WeightsWindow[] Weights { get; } + + /// + /// Disposes instance releasing it's backing buffer. + /// + public void Dispose() + { + this.dataBuffer.Dispose(); + } + + /// + /// Slices a weights value at the given positions. + /// + /// The index in destination buffer + /// The local left index value + /// The local right index value + /// The weights + public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) + { + return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs new file mode 100644 index 0000000000..b0a530514e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -0,0 +1,150 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Points to a collection of of weights allocated in . + /// + internal struct WeightsWindow + { + /// + /// The local left index position + /// + public int Left; + + /// + /// The length of the weights window + /// + public int Length; + + /// + /// The index in the destination buffer + /// + private readonly int flatStartIndex; + + /// + /// The buffer containing the weights values. + /// + private readonly Buffer buffer; + + /// + /// Initializes a new instance of the struct. + /// + /// The destination index in the buffer + /// The local left index + /// The span + /// The length of the window + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal WeightsWindow(int index, int left, Buffer2D buffer, int length) + { + this.flatStartIndex = (index * buffer.Width) + left; + this.Left = left; + this.buffer = buffer; + this.Length = length; + } + + /// + /// Gets a reference to the first item of the window. + /// + /// The reference to the first item of the window + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref float GetStartReference() + { + return ref this.buffer[this.flatStartIndex]; + } + + /// + /// Gets the span representing the portion of the that this window covers + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length); + + /// + /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. + /// + /// The input span of vectors + /// The source row position. + /// The weighted sum + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX) + { + ref float horizontalValues = ref this.GetStartReference(); + int left = this.Left; + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v.Premultiply() * weight; + } + + return result; + } + + /// + /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. + /// Applies to all input vectors. + /// + /// The input span of vectors + /// The source row position. + /// The weighted sum + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX) + { + ref float horizontalValues = ref this.GetStartReference(); + int left = this.Left; + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v.Premultiply().Expand() * weight; + } + + return result.UnPremultiply(); + } + + /// + /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x', + /// weighted by weight values, pointed by this instance. + /// + /// The buffer of input vectors in row first order + /// The row position + /// The source column position. + /// The weighted sum + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY) + { + ref float verticalValues = ref this.GetStartReference(); + int left = this.Left; + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float yw = Unsafe.Add(ref verticalValues, i); + int index = left + i + sourceY; + result += firstPassPixels[x, index] * yw; + } + + return result.UnPremultiply(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/AutoOrient.cs b/src/ImageSharp/Processing/Transforms/AutoOrient.cs index 07e5d5bc9a..b8b31ff278 100644 --- a/src/ImageSharp/Processing/Transforms/AutoOrient.cs +++ b/src/ImageSharp/Processing/Transforms/AutoOrient.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -20,68 +17,8 @@ namespace ImageSharp /// The pixel format. /// The image to auto rotate. /// The - public static Image AutoOrient(this Image source) + public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) where TPixel : struct, IPixel - { - Orientation orientation = GetExifOrientation(source); - - switch (orientation) - { - case Orientation.TopRight: - return source.Flip(FlipType.Horizontal); - - case Orientation.BottomRight: - return source.Rotate(RotateType.Rotate180); - - case Orientation.BottomLeft: - return source.Flip(FlipType.Vertical); - - case Orientation.LeftTop: - return source.Rotate(RotateType.Rotate90) - .Flip(FlipType.Horizontal); - - case Orientation.RightTop: - return source.Rotate(RotateType.Rotate90); - - case Orientation.RightBottom: - return source.Flip(FlipType.Vertical) - .Rotate(RotateType.Rotate270); - - case Orientation.LeftBottom: - return source.Rotate(RotateType.Rotate270); - - case Orientation.Unknown: - case Orientation.TopLeft: - default: - return source; - } - } - - /// - /// Returns the current EXIF orientation - /// - /// The pixel format. - /// The image to auto rotate. - /// The - private static Orientation GetExifOrientation(Image source) - where TPixel : struct, IPixel - { - if (source.MetaData.ExifProfile == null) - { - return Orientation.Unknown; - } - - ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); - if (value == null) - { - return Orientation.Unknown; - } - - var orientation = (Orientation)value.Value; - - source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); - - return orientation; - } + => source.ApplyProcessor(new AutoOrientProcessor()); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Crop.cs b/src/ImageSharp/Processing/Transforms/Crop.cs index 94e9ba1f49..3fa59c2483 100644 --- a/src/ImageSharp/Processing/Transforms/Crop.cs +++ b/src/ImageSharp/Processing/Transforms/Crop.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,11 +21,9 @@ namespace ImageSharp /// The target image width. /// The target image height. /// The - public static Image Crop(this Image source, int width, int height) + public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel - { - return Crop(source, new Rectangle(0, 0, width, height)); - } + => Crop(source, new Rectangle(0, 0, width, height)); /// /// Crops an image to the given rectangle. @@ -40,13 +34,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to retain. /// /// The - public static Image Crop(this Image source, Rectangle cropRectangle) + public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) where TPixel : struct, IPixel - { - CropProcessor processor = new CropProcessor(cropRectangle); - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new CropProcessor(cropRectangle)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/EntropyCrop.cs b/src/ImageSharp/Processing/Transforms/EntropyCrop.cs index 2f4a8e3833..cbd2b46599 100644 --- a/src/ImageSharp/Processing/Transforms/EntropyCrop.cs +++ b/src/ImageSharp/Processing/Transforms/EntropyCrop.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,13 +19,8 @@ namespace ImageSharp /// The image to crop. /// The threshold for entropic density. /// The - public static Image EntropyCrop(this Image source, float threshold = .5f) + public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold = .5f) where TPixel : struct, IPixel - { - EntropyCropProcessor processor = new EntropyCropProcessor(threshold); - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new EntropyCropProcessor(threshold)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Flip.cs b/src/ImageSharp/Processing/Transforms/Flip.cs index 1c8baebf13..e153e89f28 100644 --- a/src/ImageSharp/Processing/Transforms/Flip.cs +++ b/src/ImageSharp/Processing/Transforms/Flip.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,13 +20,8 @@ namespace ImageSharp /// The image to rotate, flip, or both. /// The to perform the flip. /// The - public static Image Flip(this Image source, FlipType flipType) + public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipType flipType) where TPixel : struct, IPixel - { - FlipProcessor processor = new FlipProcessor(flipType); - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new FlipProcessor(flipType)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs b/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs index 67010a777f..263af14ccd 100644 --- a/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs +++ b/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerated anchor positions to apply to resized images. diff --git a/src/ImageSharp/Processing/Transforms/Options/FlipType.cs b/src/ImageSharp/Processing/Transforms/Options/FlipType.cs index 3c66da697f..0129891f66 100644 --- a/src/ImageSharp/Processing/Transforms/Options/FlipType.cs +++ b/src/ImageSharp/Processing/Transforms/Options/FlipType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Provides enumeration over how a image should be flipped. diff --git a/src/ImageSharp/Processing/Transforms/Options/Orientation.cs b/src/ImageSharp/Processing/Transforms/Options/Orientation.cs index b8147348f7..9c8d96a71c 100644 --- a/src/ImageSharp/Processing/Transforms/Options/Orientation.cs +++ b/src/ImageSharp/Processing/Transforms/Options/Orientation.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the available orientation values supplied by EXIF metadata. diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs index 7eae89eac0..17a0cc428f 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing -{ - using System.Linq; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Linq; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing +{ /// /// Provides methods to help calculate the target rectangle when resizing using the /// enumeration. @@ -25,7 +23,7 @@ namespace ImageSharp.Processing /// /// The . /// - public static Rectangle CalculateTargetLocationAndBounds(ImageBase source, ResizeOptions options) + public static Rectangle CalculateTargetLocationAndBounds(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { switch (options.Mode) @@ -56,7 +54,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateCropRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateCropRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -175,7 +173,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculatePadRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculatePadRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -256,7 +254,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateBoxPadRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateBoxPadRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -343,7 +341,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateMaxRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateMaxRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -384,7 +382,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateMinRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateMinRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs index dc6092de84..c88808f758 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerated resize modes to apply to resized images. diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs index 2ca2b1c39d..d20eaefb11 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing -{ - using System.Collections.Generic; - using System.Linq; - using SixLabors.Primitives; +using System.Collections.Generic; +using System.Linq; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing +{ /// /// The resize options for resizing images against certain modes. /// @@ -37,7 +35,7 @@ namespace ImageSharp.Processing /// /// Gets or sets the sampler to perform the resize operation. /// - public IResampler Sampler { get; set; } = new BicubicResampler(); + public IResampler Sampler { get; set; } = KnownResamplers.Bicubic; /// /// Gets or sets a value indicating whether to compress diff --git a/src/ImageSharp/Processing/Transforms/Options/RotateType.cs b/src/ImageSharp/Processing/Transforms/Options/RotateType.cs index 9f09cec280..9f6d45f2bf 100644 --- a/src/ImageSharp/Processing/Transforms/Options/RotateType.cs +++ b/src/ImageSharp/Processing/Transforms/Options/RotateType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Provides enumeration over how the image should be rotated. diff --git a/src/ImageSharp/Processing/Transforms/Pad.cs b/src/ImageSharp/Processing/Transforms/Pad.cs index 8138fd7654..eb0f2e9c27 100644 --- a/src/ImageSharp/Processing/Transforms/Pad.cs +++ b/src/ImageSharp/Processing/Transforms/Pad.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -26,7 +22,7 @@ namespace ImageSharp /// The new width. /// The new height. /// The . - public static Image Pad(this Image source, int width, int height) + public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel { ResizeOptions options = new ResizeOptions diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs index e40bf59f92..be9de9edaa 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the bicubic kernel algorithm W(x) as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs index f174ef48bd..5aab0d07fa 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the box algorithm. Similar to nearest neighbor when upscaling. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs index 0725ecb537..1c84676188 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The Catmull-Rom filter is a well known standard Cubic Filter often used as a interpolation function. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs index 66cc36a21f..33435059f1 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The Hermite filter is type of smoothed triangular interpolation Filter, diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs index 6339d9d8ea..9a128a05be 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Encapsulates an interpolation algorithm for resampling images. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs b/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs new file mode 100644 index 0000000000..2da98497b5 --- /dev/null +++ b/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs @@ -0,0 +1,96 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Contains reusable static instances of known resampling algorithms + /// + public static class KnownResamplers + { + /// + /// Gets the Bicubic sampler that implements the bicubic kernel algorithm W(x) + /// + public static IResampler Bicubic { get; } = new BicubicResampler(); + + /// + /// Gets the Box sampler that implements the box algorithm. Similar to nearest neighbor when upscaling. + /// When downscaling the pixels will average, merging pixels together. + /// + public static IResampler Box { get; } = new BoxResampler(); + + /// + /// Gets the Catmull-Rom sampler, a well known standard Cubic Filter often used as a interpolation function + /// + public static IResampler CatmullRom { get; } = new CatmullRomResampler(); + + /// + /// Gets the Hermite sampler. A type of smoothed triangular interpolation filter that rounds off strong edges while + /// preserving flat 'color levels' in the original image. + /// + public static IResampler Hermite { get; } = new HermiteResampler(); + + /// + /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 2 pixels. + /// This algorithm provides sharpened results when compared to others when downsampling. + /// + public static IResampler Lanczos2 { get; } = new Lanczos2Resampler(); + + /// + /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 3 pixels + /// This algorithm provides sharpened results when compared to others when downsampling. + /// + public static IResampler Lanczos3 { get; } = new Lanczos3Resampler(); + + /// + /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 5 pixels + /// This algorithm provides sharpened results when compared to others when downsampling. + /// + public static IResampler Lanczos5 { get; } = new Lanczos5Resampler(); + + /// + /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 8 pixels + /// This algorithm provides sharpened results when compared to others when downsampling. + /// + public static IResampler Lanczos8 { get; } = new Lanczos8Resampler(); + + /// + /// Gets the Mitchell-Netravali sampler. This seperable cubic algorithm yields a very good equilibrium between + /// detail preservation (sharpness) and smoothness. + /// + public static IResampler MitchellNetravali { get; } = new MitchellNetravaliResampler(); + + /// + /// Gets the Nearest-Neighbour sampler that implements the nearest neighbor algorithm. This uses a very fast, unscaled filter + /// which will select the closest pixel to the new pixels position. + /// + public static IResampler NearestNeighbor { get; } = new NearestNeighborResampler(); + + /// + /// Gets the Robidoux sampler. This algorithm developed by Nicolas Robidoux providing a very good equilibrium between + /// detail preservation (sharpness) and smoothness comprable to . + /// + public static IResampler Robidoux { get; } = new RobidouxResampler(); + + /// + /// Gets the Robidoux Sharp sampler. A sharpend form of the sampler + /// + public static IResampler RobidouxSharp { get; } = new RobidouxSharpResampler(); + + /// + /// Gets the Spline sampler. A seperable cubic algorithm similar to but yielding smoother results. + /// + public static IResampler Spline { get; } = new SplineResampler(); + + /// + /// Gets the Triangle sampler, otherwise known as Bilinear. This interpolation algorithm can be used where perfect image transformation + /// with pixel matching is impossible, so that one can calculate and assign appropriate intensity values to pixels + /// + public static IResampler Triangle { get; } = new TriangleResampler(); + + /// + /// Gets the Welch sampler. A high speed algorthm that delivers very sharpened results. + /// + public static IResampler Welch { get; } = new WelchResampler(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs index a46f38df42..29568db021 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on @@ -25,7 +23,7 @@ namespace ImageSharp.Processing if (x < 2F) { - return MathF.SinC(x) * MathF.SinC(x / 2F); + return ImageMaths.SinC(x) * ImageMaths.SinC(x / 2F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs index 3ad01b2b7b..492ef69e4c 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on @@ -25,7 +23,7 @@ namespace ImageSharp.Processing if (x < 3F) { - return MathF.SinC(x) * MathF.SinC(x / 3F); + return ImageMaths.SinC(x) * ImageMaths.SinC(x / 3F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs index 9bb8bdd16f..cae152a53c 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on @@ -25,7 +23,7 @@ namespace ImageSharp.Processing if (x < 5F) { - return MathF.SinC(x) * MathF.SinC(x / 5F); + return ImageMaths.SinC(x) * ImageMaths.SinC(x / 5F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs index af8c2c111c..b390c55419 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on @@ -25,7 +23,7 @@ namespace ImageSharp.Processing if (x < 8F) { - return MathF.SinC(x) * MathF.SinC(x / 8F); + return ImageMaths.SinC(x) * ImageMaths.SinC(x / 8F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs index 38ca614c4a..df351d9505 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the mitchell algorithm as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs index f1e5ab8fdb..7a7785be36 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the nearest neighbor algorithm. This uses an unscaled filter diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs index 1b060e6e3f..bd28d8da46 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Robidoux algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs index 86c7b0517c..a345da3f42 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Robidoux Sharp algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs index abd5b835c6..ac5e2dedba 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the spline algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs index bf88b65df8..842da87e06 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the triangle (bilinear) algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs index 33a2cc825e..9e18a24710 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the welch algorithm. @@ -24,10 +22,10 @@ namespace ImageSharp.Processing if (x < 3F) { - return MathF.SinC(x) * (1F - (x * x / 9F)); + return ImageMaths.SinC(x) * (1F - (x * x / 9F)); } return 0F; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index e00faf10ea..832b02dea7 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,41 +22,45 @@ namespace ImageSharp /// The resize options. /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, ResizeOptions options) + public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) where TPixel : struct, IPixel { - // Ensure size is populated across both dimensions. - if (options.Size.Width == 0 && options.Size.Height > 0) + return source.Apply(img => { - options.Size = new Size((int)MathF.Round(source.Width * options.Size.Height / (float)source.Height), options.Size.Height); - } - - if (options.Size.Height == 0 && options.Size.Width > 0) - { - options.Size = new Size(options.Size.Width, (int)MathF.Round(source.Height * options.Size.Width / (float)source.Width)); - } - - Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(source, options); - - return Resize(source, options.Size.Width, options.Size.Height, options.Sampler, source.Bounds, targetRectangle, options.Compand); + // Cheat and bound through a run, inside here we should just be mutating, this really needs moving over to a processor + // Ensure size is populated across both dimensions. + if (options.Size.Width == 0 && options.Size.Height > 0) + { + options.Size = new Size((int)MathF.Round(img.Width * options.Size.Height / (float)img.Height), options.Size.Height); + } + + if (options.Size.Height == 0 && options.Size.Width > 0) + { + options.Size = new Size(options.Size.Width, (int)MathF.Round(img.Height * options.Size.Width / (float)img.Width)); + } + + Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img.Frames.RootFrame, options); + + img.Mutate(x => Resize(x, options.Size.Width, options.Size.Height, options.Sampler, targetRectangle, options.Compand)); + }); } /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. /// The target image size. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, Size size) + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) where TPixel : struct, IPixel { - return Resize(source, size.Width, size.Height, new BicubicResampler(), false); + return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); } /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. @@ -66,10 +68,10 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, Size size, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) where TPixel : struct, IPixel { - return Resize(source, size.Width, size.Height, new BicubicResampler(), compand); + return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); } /// @@ -81,10 +83,10 @@ namespace ImageSharp /// The target image height. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel { - return Resize(source, width, height, new BicubicResampler(), false); + return Resize(source, width, height, KnownResamplers.Bicubic, false); } /// @@ -97,10 +99,10 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) where TPixel : struct, IPixel { - return Resize(source, width, height, new BicubicResampler(), compand); + return Resize(source, width, height, KnownResamplers.Bicubic, compand); } /// @@ -113,12 +115,28 @@ namespace ImageSharp /// The to perform the resampling. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) where TPixel : struct, IPixel { return Resize(source, width, height, sampler, false); } + /// + /// Resizes an image to the given width and height with the given sampler. + /// + /// The pixel format. + /// The image to resize. + /// The target image size. + /// The to perform the resampling. + /// Whether to compress and expand the image color-space to gamma correct the image during processing. + /// The + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) + where TPixel : struct, IPixel + { + return Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); + } + /// /// Resizes an image to the given width and height with the given sampler. /// @@ -130,10 +148,10 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) where TPixel : struct, IPixel { - return Resize(source, width, height, sampler, source.Bounds, new Rectangle(0, 0, width, height), compand); + return Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); } /// @@ -154,28 +172,69 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) where TPixel : struct, IPixel { - if (width == 0 && height > 0) + return source.Apply(img => { - width = (int)MathF.Round(source.Width * height / (float)source.Height); - targetRectangle.Width = width; - } + // TODO : Stop cheating here and move this stuff into the processors itself + if (width == 0 && height > 0) + { + width = (int)MathF.Round(img.Width * height / (float)img.Height); + targetRectangle.Width = width; + } + + if (height == 0 && width > 0) + { + height = (int)MathF.Round(img.Height * width / (float)img.Width); + targetRectangle.Height = height; + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); + }); + } - if (height == 0 && width > 0) + /// + /// Resizes an image to the given width and height with the given sampler and + /// source rectangle. + /// + /// The pixel format. + /// The image to resize. + /// The target image width. + /// The target image height. + /// The to perform the resampling. + /// + /// The structure that specifies the portion of the target image object to draw to. + /// + /// Whether to compress and expand the image color-space to gamma correct the image during processing. + /// The + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle targetRectangle, bool compand) + where TPixel : struct, IPixel + { + return source.Apply(img => { - height = (int)MathF.Round(source.Height * width / (float)source.Width); - targetRectangle.Height = height; - } - - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - var processor = new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }; - - source.ApplyProcessor(processor, sourceRectangle); - return source; + // TODO : stop cheating here and move this stuff into the processors itself + if (width == 0 && height > 0) + { + width = (int)MathF.Round(img.Width * height / (float)img.Height); + targetRectangle.Width = width; + } + + if (height == 0 && width > 0) + { + height = (int)MathF.Round(img.Height * width / (float)img.Width); + targetRectangle.Height = height; + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand })); + }); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index af7c06a27e..69fb7ebf0c 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -1,63 +1,49 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { /// - /// Rotates an image by the given angle in degrees, expanding the image to fit the rotated result. + /// Rotates and flips an image by the given instructions. /// /// The pixel format. /// The image to rotate. - /// The angle in degrees to perform the rotation. + /// The to perform the rotation. /// The - public static Image Rotate(this Image source, float degrees) + public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateType rotateType) where TPixel : struct, IPixel - { - return Rotate(source, degrees, true); - } + => Rotate(source, (float)rotateType); /// - /// Rotates and flips an image by the given instructions. + /// Rotates an image by the given angle in degrees. /// /// The pixel format. /// The image to rotate. - /// The to perform the rotation. + /// The angle in degrees to perform the rotation. /// The - public static Image Rotate(this Image source, RotateType rotateType) + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) where TPixel : struct, IPixel - { - return Rotate(source, (float)rotateType, false); - } + => Rotate(source, degrees, KnownResamplers.Bicubic); /// - /// Rotates an image by the given angle in degrees. + /// Rotates an image by the given angle in degrees using the specified sampling algorithm. /// /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. - /// Whether to expand the image to fit the rotated result. + /// The to perform the resampling. /// The - public static Image Rotate(this Image source, float degrees, bool expand) + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) where TPixel : struct, IPixel - { - RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new RotateProcessor(degrees, sampler)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/RotateFlip.cs b/src/ImageSharp/Processing/Transforms/RotateFlip.cs index 805deb8d16..2ddcb151b2 100644 --- a/src/ImageSharp/Processing/Transforms/RotateFlip.cs +++ b/src/ImageSharp/Processing/Transforms/RotateFlip.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The to perform the rotation. /// The to perform the flip. /// The - public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType) + public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateType rotateType, FlipType flipType) where TPixel : struct, IPixel { return source.Rotate(rotateType).Flip(flipType); diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index d42d79225b..0613a690b8 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -1,51 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { /// - /// Skews an image by the given angles in degrees, expanding the image to fit the skewed result. + /// Skews an image by the given angles in degrees. /// /// The pixel format. /// The image to skew. - /// The angle in degrees to perform the rotation along the x-axis. - /// The angle in degrees to perform the rotation along the y-axis. + /// The angle in degrees to perform the skew along the x-axis. + /// The angle in degrees to perform the skew along the y-axis. /// The - public static Image Skew(this Image source, float degreesX, float degreesY) + public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) where TPixel : struct, IPixel - { - return Skew(source, degreesX, degreesY, true); - } + => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); /// - /// Skews an image by the given angles in degrees. + /// Skews an image by the given angles in degrees using the specified sampling algorithm. /// /// The pixel format. /// The image to skew. - /// The angle in degrees to perform the rotation along the x-axis. - /// The angle in degrees to perform the rotation along the y-axis. - /// Whether to expand the image to fit the skewed result. + /// The angle in degrees to perform the skew along the x-axis. + /// The angle in degrees to perform the skew along the y-axis. + /// The to perform the resampling. /// The - public static Image Skew(this Image source, float degreesX, float degreesY, bool expand) + public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) where TPixel : struct, IPixel - { - SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Transform.cs b/src/ImageSharp/Processing/Transforms/Transform.cs new file mode 100644 index 0000000000..e39da8dc0f --- /dev/null +++ b/src/ImageSharp/Processing/Transforms/Transform.cs @@ -0,0 +1,122 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Transforms an image by the given matrix. + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The + public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix) + where TPixel : struct, IPixel + => Transform(source, matrix, KnownResamplers.NearestNeighbor); + + /// + /// Transforms an image by the given matrix using the specified sampling algorithm. + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The to perform the resampling. + /// The + public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix, IResampler sampler) + where TPixel : struct, IPixel + => Transform(source, matrix, sampler, Size.Empty); + + /// + /// Transforms an image by the given matrix using the specified sampling algorithm + /// and a rectangle defining the transform origin in the source image and the size of the result image. + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The to perform the resampling. + /// + /// The rectangle defining the transform origin in the source image, and the size of the result image. + /// + /// The + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + Matrix3x2 matrix, + IResampler sampler, + Rectangle rectangle) + where TPixel : struct, IPixel + { + var t = Matrix3x2.CreateTranslation(-rectangle.Location); + Matrix3x2 combinedMatrix = t * matrix; + return source.ApplyProcessor(new AffineTransformProcessor(combinedMatrix, sampler, rectangle.Size)); + } + + /// + /// Transforms an image by the given matrix using the specified sampling algorithm, + /// cropping or extending the image according to . + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The to perform the resampling. + /// The size of the destination image. + /// The + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + Matrix3x2 matrix, + IResampler sampler, + Size destinationSize) + where TPixel : struct, IPixel + { + return source.ApplyProcessor(new AffineTransformProcessor(matrix, sampler, destinationSize)); + } + + /// + /// Transforms an image by the given matrix. + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The + internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix) + where TPixel : struct, IPixel + => Transform(source, matrix, KnownResamplers.NearestNeighbor); + + /// + /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. + /// TODO: Doesn't work yet! Implement tests + Finish implementation + Document Matrix4x4 behavior + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The to perform the resampling. + /// The + internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler) + where TPixel : struct, IPixel + => Transform(source, matrix, sampler, Rectangle.Empty); + + /// + /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. + /// TODO: Doesn't work yet! Implement tests + Finish implementation + Document Matrix4x4 behavior + /// + /// The pixel format. + /// The image to transform. + /// The transformation matrix. + /// The to perform the resampling. + /// The rectangle to constrain the transformed image to. + /// The + internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler, Rectangle rectangle) + where TPixel : struct, IPixel + => source.ApplyProcessor(new ProjectiveTransformProcessor(matrix, sampler, rectangle)); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/TransformHelpers.cs b/src/ImageSharp/Processing/Transforms/TransformHelpers.cs new file mode 100644 index 0000000000..bfb06c4707 --- /dev/null +++ b/src/ImageSharp/Processing/Transforms/TransformHelpers.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Contains helper methods for working with affine and non-affine transforms + /// + internal class TransformHelpers + { + /// + /// Updates the dimensional metadata of a transformed image + /// + /// The pixel format. + /// The image to update + public static void UpdateDimensionalMetData(Image image) + where TPixel : struct, IPixel + { + ExifProfile profile = image.MetaData.ExifProfile; + if (profile == null) + { + return; + } + + // Removing the previously stored value allows us to set a value with our own data tag if required. + if (profile.GetValue(ExifTag.PixelXDimension) != null) + { + profile.RemoveValue(ExifTag.PixelXDimension); + + if (image.Width <= ushort.MaxValue) + { + profile.SetValue(ExifTag.PixelXDimension, (ushort)image.Width); + } + else + { + profile.SetValue(ExifTag.PixelXDimension, (uint)image.Width); + } + } + + if (profile.GetValue(ExifTag.PixelYDimension) != null) + { + profile.RemoveValue(ExifTag.PixelYDimension); + + if (image.Height <= ushort.MaxValue) + { + profile.SetValue(ExifTag.PixelYDimension, (ushort)image.Height); + } + else + { + profile.SetValue(ExifTag.PixelYDimension, (uint)image.Height); + } + } + } + + /// + /// Returns the bounding relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// + /// The . + /// + public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) + { + // Calculate the position of the four corners in world space by applying + // The world matrix to the four corners in object space (0, 0, width, height) + var tl = Vector2.Transform(Vector2.Zero, matrix); + var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix); + var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix); + var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix); + + return GetBoundingRectangle(tl, tr, bl, br); + } + + /// + /// Returns the bounding relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// + /// The . + /// + public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix4x4 matrix) + { + // Calculate the position of the four corners in world space by applying + // The world matrix to the four corners in object space (0, 0, width, height) + var tl = Vector2.Transform(Vector2.Zero, matrix); + var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix); + var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix); + var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix); + + return GetBoundingRectangle(tl, tr, bl, br); + } + + private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) + { + // Find the minimum and maximum "corners" based on the given vectors + float minX = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); + float maxX = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); + float minY = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); + float maxY = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); + float sizeX = maxX - minX + .5F; + float sizeY = maxY - minY + .5F; + + return new Rectangle((int)(MathF.Ceiling(minX) - .5F), (int)(MathF.Ceiling(minY) - .5F), (int)MathF.Floor(sizeX), (int)MathF.Floor(sizeY)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Properties/AssemblyInfo.cs b/src/ImageSharp/Properties/AssemblyInfo.cs index e791dff5a5..7b8f933b0c 100644 --- a/src/ImageSharp/Properties/AssemblyInfo.cs +++ b/src/ImageSharp/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Runtime.CompilerServices; // Ensure the other projects can see the internal helpers -[assembly: InternalsVisibleTo("ImageSharp.Drawing")] \ No newline at end of file +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Box.cs b/src/ImageSharp/Quantizers/Box.cs index 7f2a320873..cd1936b653 100644 --- a/src/ImageSharp/Quantizers/Box.cs +++ b/src/ImageSharp/Quantizers/Box.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers +namespace SixLabors.ImageSharp.Quantizers { /// /// Represents a box color cube. - /// TODO: This should be a struct for performance /// - internal sealed class Box + internal struct Box { /// /// Gets or sets the min red value, exclusive. @@ -56,4 +53,4 @@ namespace ImageSharp.Quantizers /// public int Volume { get; set; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs index b7cf2d469a..82f6146a3e 100644 --- a/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Provides methods for for allowing quantization of images pixels with configurable dithering. /// @@ -23,7 +21,7 @@ namespace ImageSharp.Quantizers /// /// A representing a quantized version of the image pixels. /// - QuantizedImage Quantize(ImageBase image, int maxColors); + QuantizedImage Quantize(ImageFrame image, int maxColors); } /// diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index 8ebea85334..d646a680ea 100644 --- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -1,21 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers.Base; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Encapsulates methods to calculate the color palette if an image using an Octree pattern. /// /// /// The pixel format. - public sealed class OctreeQuantizer : Quantizer + public sealed class OctreeQuantizer : QuantizerBase where TPixel : struct, IPixel { /// @@ -23,11 +23,6 @@ namespace ImageSharp.Quantizers /// private readonly Dictionary colorMap = new Dictionary(); - /// - /// The pixel buffer, used to reduce allocations. - /// - private readonly byte[] pixelBuffer = new byte[4]; - /// /// Stores the tree /// @@ -43,6 +38,11 @@ namespace ImageSharp.Quantizers /// private TPixel[] palette; + /// + /// The transparent index + /// + private byte transparentIndex; + /// /// Initializes a new instance of the class. /// @@ -56,29 +56,31 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { this.colors = (byte)maxColors.Clamp(1, 255); this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors)); this.palette = null; + this.colorMap.Clear(); return base.Quantize(image, this.colors); } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. TPixel sourcePixel = source[0, 0]; TPixel previousPixel = sourcePixel; - byte pixelValue = this.QuantizePixel(sourcePixel); + var rgba = default(Rgba32); + byte pixelValue = this.QuantizePixel(sourcePixel, ref rgba); TPixel[] colorPalette = this.GetPalette(); TPixel transformedPixel = colorPalette[pixelValue]; for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -91,7 +93,7 @@ namespace ImageSharp.Quantizers if (!previousPixel.Equals(sourcePixel)) { // Quantize the pixel - pixelValue = this.QuantizePixel(sourcePixel); + pixelValue = this.QuantizePixel(sourcePixel, ref rgba); // And setup the previous pointer previousPixel = sourcePixel; @@ -105,7 +107,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); } output[(y * source.Width) + x] = pixelValue; @@ -117,24 +119,57 @@ namespace ImageSharp.Quantizers protected override void InitialQuantizePixel(TPixel pixel) { // Add the color to the Octree - this.octree.AddColor(pixel, this.pixelBuffer); + var rgba = default(Rgba32); + this.octree.AddColor(pixel, ref rgba); } /// protected override TPixel[] GetPalette() { - return this.palette ?? (this.palette = this.octree.Palletize(Math.Max(this.colors, (byte)1))); + if (this.palette == null) + { + this.palette = this.octree.Palletize(Math.Max(this.colors, (byte)1)); + this.transparentIndex = this.GetTransparentIndex(); + } + + return this.palette; + } + + /// + /// Returns the index of the first instance of the transparent color in the palette. + /// + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte GetTransparentIndex() + { + // Transparent pixels are much more likely to be found at the end of a palette + int index = this.colors; + var trans = default(Rgba32); + for (int i = this.palette.Length - 1; i >= 0; i--) + { + this.palette[i].ToRgba32(ref trans); + + if (trans.Equals(default(Rgba32))) + { + index = i; + } + } + + return (byte)index; } /// /// Process the pixel in the second pass of the algorithm /// /// The pixel to quantize + /// The color to compare against /// /// The quantized value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte QuantizePixel(TPixel pixel) + private byte QuantizePixel(TPixel pixel, ref Rgba32 rgba) { if (this.Dither) { @@ -143,13 +178,13 @@ namespace ImageSharp.Quantizers return this.GetClosestPixel(pixel, this.palette, this.colorMap); } - pixel.ToXyzwBytes(this.pixelBuffer, 0); - if (this.pixelBuffer[3] == 0) + pixel.ToRgba32(ref rgba); + if (rgba.Equals(default(Rgba32))) { - return this.colors; + return this.transparentIndex; } - return (byte)this.octree.GetPaletteIndex(pixel, this.pixelBuffer); + return (byte)this.octree.GetPaletteIndex(pixel, ref rgba); } /// @@ -232,8 +267,8 @@ namespace ImageSharp.Quantizers /// Add a given color value to the Octree /// /// The pixel data. - /// The buffer array. - public void AddColor(TPixel pixel, byte[] buffer) + /// The color. + public void AddColor(TPixel pixel, ref Rgba32 rgba) { // Check if this request is for the same color as the last if (this.previousColor.Equals(pixel)) @@ -243,18 +278,18 @@ namespace ImageSharp.Quantizers if (this.previousNode == null) { this.previousColor = pixel; - this.root.AddColor(pixel, this.maxColorBits, 0, this, buffer); + this.root.AddColor(pixel, this.maxColorBits, 0, this, ref rgba); } else { // Just update the previous node - this.previousNode.Increment(pixel, buffer); + this.previousNode.Increment(pixel, ref rgba); } } else { this.previousColor = pixel; - this.root.AddColor(pixel, this.maxColorBits, 0, this, buffer); + this.root.AddColor(pixel, this.maxColorBits, 0, this, ref rgba); } } @@ -286,13 +321,13 @@ namespace ImageSharp.Quantizers /// Get the palette index for the passed color /// /// The pixel data. - /// The buffer array. + /// The color to map to. /// /// The . /// - public int GetPaletteIndex(TPixel pixel, byte[] buffer) + public int GetPaletteIndex(TPixel pixel, ref Rgba32 rgba) { - return this.root.GetPaletteIndex(pixel, 0, buffer); + return this.root.GetPaletteIndex(pixel, 0, ref rgba); } /// @@ -414,17 +449,17 @@ namespace ImageSharp.Quantizers /// /// Add a color into the tree /// - /// The color + /// The pixel color /// The number of significant color bits /// The level in the tree /// The tree to which this node belongs - /// The buffer array. - public void AddColor(TPixel pixel, int colorBits, int level, Octree octree, byte[] buffer) + /// The color to map to. + public void AddColor(TPixel pixel, int colorBits, int level, Octree octree, ref Rgba32 rgba) { // Update the color information if this is a leaf if (this.leaf) { - this.Increment(pixel, buffer); + this.Increment(pixel, ref rgba); // Setup the previous node octree.TrackPrevious(this); @@ -433,11 +468,11 @@ namespace ImageSharp.Quantizers { // Go to the next level down in the tree int shift = 7 - level; - pixel.ToXyzwBytes(buffer, 0); + pixel.ToRgba32(ref rgba); - int index = ((buffer[2] & Mask[level]) >> (shift - 2)) | - ((buffer[1] & Mask[level]) >> (shift - 1)) | - ((buffer[0] & Mask[level]) >> shift); + int index = ((rgba.B & Mask[level]) >> (shift - 2)) | + ((rgba.G & Mask[level]) >> (shift - 1)) | + ((rgba.R & Mask[level]) >> shift); OctreeNode child = this.children[index]; @@ -449,7 +484,7 @@ namespace ImageSharp.Quantizers } // Add the color to the child node - child.AddColor(pixel, colorBits, level + 1, octree, buffer); + child.AddColor(pixel, colorBits, level + 1, octree, ref rgba); } } @@ -523,26 +558,26 @@ namespace ImageSharp.Quantizers /// /// The pixel data. /// The level. - /// The buffer array. + /// The color to map to. /// /// The representing the index of the pixel in the palette. /// - public int GetPaletteIndex(TPixel pixel, int level, byte[] buffer) + public int GetPaletteIndex(TPixel pixel, int level, ref Rgba32 rgba) { int index = this.paletteIndex; if (!this.leaf) { int shift = 7 - level; - pixel.ToXyzwBytes(buffer, 0); + pixel.ToRgba32(ref rgba); - int pixelIndex = ((buffer[2] & Mask[level]) >> (shift - 2)) | - ((buffer[1] & Mask[level]) >> (shift - 1)) | - ((buffer[0] & Mask[level]) >> shift); + int pixelIndex = ((rgba.B & Mask[level]) >> (shift - 2)) | + ((rgba.G & Mask[level]) >> (shift - 1)) | + ((rgba.R & Mask[level]) >> shift); if (this.children[pixelIndex] != null) { - index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1, buffer); + index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1, ref rgba); } else { @@ -557,14 +592,14 @@ namespace ImageSharp.Quantizers /// Increment the pixel count and add to the color information /// /// The pixel to add. - /// The buffer array. - public void Increment(TPixel pixel, byte[] buffer) + /// The color to map to. + public void Increment(TPixel pixel, ref Rgba32 rgba) { - pixel.ToXyzwBytes(buffer, 0); + pixel.ToRgba32(ref rgba); this.pixelCount++; - this.red += buffer[0]; - this.green += buffer[1]; - this.blue += buffer[2]; + this.red += rgba.R; + this.green += rgba.G; + this.blue += rgba.B; } } } diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index d121dc6ae7..0b95c09a62 100644 --- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -1,21 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers.Base; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Encapsulates methods to create a quantized image based upon the given palette. /// /// /// The pixel format. - public sealed class PaletteQuantizer : Quantizer + public sealed class PaletteQuantizer : QuantizerBase where TPixel : struct, IPixel { /// @@ -55,14 +55,15 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); + this.colorMap.Clear(); return base.Quantize(image, maxColors); } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. @@ -74,7 +75,7 @@ namespace ImageSharp.Quantizers for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -101,7 +102,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Quantizers/Quantization.cs b/src/ImageSharp/Quantizers/Quantization.cs index 039404384d..df55d3e87a 100644 --- a/src/ImageSharp/Quantizers/Quantization.cs +++ b/src/ImageSharp/Quantizers/Quantization.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Provides enumeration over how an image should be quantized. diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index a235092787..d99d4af34c 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The quantization mode to apply to perform the operation. /// The maximum number of colors to return. Defaults to 256. /// The . - public static Image Quantize(this Image source, Quantization mode = Quantization.Octree, int maxColors = 256) + public static IImageProcessingContext Quantize(this IImageProcessingContext source, Quantization mode = Quantization.Octree, int maxColors = 256) where TPixel : struct, IPixel { IQuantizer quantizer; @@ -54,31 +52,34 @@ namespace ImageSharp /// The quantizer to apply to perform the operation. /// The maximum number of colors to return. /// The . - public static Image Quantize(this Image source, IQuantizer quantizer, int maxColors) + public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer, int maxColors) where TPixel : struct, IPixel { - QuantizedImage quantized = quantizer.Quantize(source, maxColors); - int palleteCount = quantized.Palette.Length - 1; - - using (PixelAccessor pixels = new PixelAccessor(quantized.Width, quantized.Height)) + return source.Apply(img => { - Parallel.For( - 0, - pixels.Height, - source.Configuration.ParallelOptions, - y => - { - for (int x = 0; x < pixels.Width; x++) + // TODO : move helper logic into the processor + QuantizedImage quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); + int palleteCount = quantized.Palette.Length - 1; + + using (var pixels = new PixelAccessor(quantized.Width, quantized.Height)) + { + Parallel.For( + 0, + pixels.Height, + img.GetConfiguration().ParallelOptions, + y => { - int i = x + (y * pixels.Width); - TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; - pixels[x, y] = color; - } - }); + for (int x = 0; x < pixels.Width; x++) + { + int i = x + (y * pixels.Width); + TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; + pixels[x, y] = color; + } + }); - source.SwapPixelsBuffers(pixels); - return source; - } + img.Frames[0].SwapPixelsBuffers(pixels); + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs index c8b2c7df60..52cf1a8d9a 100644 --- a/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Represents a quantized image where the pixels indexed by a color palette. /// diff --git a/src/ImageSharp/Quantizers/Quantizer{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs similarity index 85% rename from src/ImageSharp/Quantizers/Quantizer{TPixel}.cs rename to src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs index 2e3ea7a543..20ba2e637e 100644 --- a/src/ImageSharp/Quantizers/Quantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs @@ -1,22 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Collections.Generic; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Quantizers.Base +{ /// /// Encapsulates methods to calculate the color palette of an image. /// /// The pixel format. - public abstract class Quantizer : IQuantizer + public abstract class QuantizerBase : IQuantizer where TPixel : struct, IPixel { /// @@ -25,7 +24,7 @@ namespace ImageSharp.Quantizers private readonly bool singlePass; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// If true, the quantization only needs to loop through the source pixels once @@ -35,7 +34,7 @@ namespace ImageSharp.Quantizers /// only call the 'QuantizeImage' function. If two passes are required, the code will call 'InitialQuantizeImage' /// and then 'QuantizeImage'. /// - protected Quantizer(bool singlePass) + protected QuantizerBase(bool singlePass) { this.singlePass = singlePass; } @@ -44,10 +43,10 @@ namespace ImageSharp.Quantizers public bool Dither { get; set; } = true; /// - public IErrorDiffuser DitherType { get; set; } = new SierraLite(); + public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser(); /// - public virtual QuantizedImage Quantize(ImageBase image, int maxColors) + public virtual QuantizedImage Quantize(ImageFrame image, int maxColors) { Guard.NotNull(image, nameof(image)); @@ -70,7 +69,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // We clone the image as we don't want to alter the original. - using (var clone = new Image(image)) + using (ImageFrame clone = image.Clone()) { this.SecondPass(clone, quantizedPixels, width, height); } @@ -89,12 +88,12 @@ namespace ImageSharp.Quantizers /// The source data /// The width in pixels of the image. /// The height in pixels of the image. - protected virtual void FirstPass(ImageBase source, int width, int height) + protected virtual void FirstPass(ImageFrame source, int width, int height) { // Loop through each row for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -112,7 +111,7 @@ namespace ImageSharp.Quantizers /// The output pixel array /// The width in pixels of the image /// The height in pixels of the image - protected abstract void SecondPass(ImageBase source, byte[] output, int width, int height); + protected abstract void SecondPass(ImageFrame source, byte[] output, int width, int height); /// /// Override this to process the pixel in the first pass of the algorithm diff --git a/src/ImageSharp/Quantizers/WuArrayPool.cs b/src/ImageSharp/Quantizers/WuArrayPool.cs index bd8ee9d6b9..04a70637b6 100644 --- a/src/ImageSharp/Quantizers/WuArrayPool.cs +++ b/src/ImageSharp/Quantizers/WuArrayPool.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System.Buffers; +using System.Buffers; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Provides array pooling for the . /// This is a separate class so that the pools can be shared accross multiple generic quantizer instaces. diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index aecca771c1..f08114574e 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -1,17 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Buffers; - using System.Collections.Generic; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers.Base; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// An implementation of Wu's color quantizer with alpha channel. /// @@ -32,7 +32,7 @@ namespace ImageSharp.Quantizers /// /// /// The pixel format. - public class WuQuantizer : Quantizer + public class WuQuantizer : QuantizerBase where TPixel : struct, IPixel { /// @@ -60,11 +60,6 @@ namespace ImageSharp.Quantizers /// private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; - /// - /// A buffer for storing pixels - /// - private readonly byte[] rgbaBuffer = new byte[4]; - /// /// A lookup table for colors /// @@ -133,12 +128,13 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Guard.NotNull(image, nameof(image)); this.colors = maxColors.Clamp(1, 255); this.palette = null; + this.colorMap.Clear(); try { @@ -172,20 +168,19 @@ namespace ImageSharp.Quantizers this.palette = new TPixel[this.colors]; for (int k = 0; k < this.colors; k++) { - this.Mark(this.colorCube[k], (byte)k); + this.Mark(ref this.colorCube[k], (byte)k); - float weight = Volume(this.colorCube[k], this.vwt); + float weight = Volume(ref this.colorCube[k], this.vwt); if (MathF.Abs(weight) > Constants.Epsilon) { - float r = Volume(this.colorCube[k], this.vmr) / weight; - float g = Volume(this.colorCube[k], this.vmg) / weight; - float b = Volume(this.colorCube[k], this.vmb) / weight; - float a = Volume(this.colorCube[k], this.vma) / weight; - - var color = default(TPixel); - color.PackFromVector4(new Vector4(r, g, b, a) / 255F); - this.palette[k] = color; + float r = Volume(ref this.colorCube[k], this.vmr); + float g = Volume(ref this.colorCube[k], this.vmg); + float b = Volume(ref this.colorCube[k], this.vmb); + float a = Volume(ref this.colorCube[k], this.vma); + + ref TPixel color = ref this.palette[k]; + color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F); } } } @@ -198,30 +193,28 @@ namespace ImageSharp.Quantizers { // Add the color to a 3-D color histogram. // Colors are expected in r->g->b->a format - pixel.ToXyzwBytes(this.rgbaBuffer, 0); - - byte r = this.rgbaBuffer[0]; - byte g = this.rgbaBuffer[1]; - byte b = this.rgbaBuffer[2]; - byte a = this.rgbaBuffer[3]; - - int inr = r >> (8 - IndexBits); - int ing = g >> (8 - IndexBits); - int inb = b >> (8 - IndexBits); - int ina = a >> (8 - IndexAlphaBits); - - int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); - - this.vwt[ind]++; - this.vmr[ind] += r; - this.vmg[ind] += g; - this.vmb[ind] += b; - this.vma[ind] += a; - this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a); + var rgba = default(Rgba32); + pixel.ToRgba32(ref rgba); + + int r = rgba.R >> (8 - IndexBits); + int g = rgba.G >> (8 - IndexBits); + int b = rgba.B >> (8 - IndexBits); + int a = rgba.A >> (8 - IndexAlphaBits); + + int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); + + this.vwt[index]++; + this.vmr[index] += rgba.R; + this.vmg[index] += rgba.G; + this.vmb[index] += rgba.B; + this.vma[index] += rgba.A; + + var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A); + this.m2[index] += Vector4.Dot(vector, vector); } /// - protected override void FirstPass(ImageBase source, int width, int height) + protected override void FirstPass(ImageFrame source, int width, int height) { // Build up the 3-D color histogram // Loop through each row @@ -240,7 +233,7 @@ namespace ImageSharp.Quantizers } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. @@ -252,7 +245,7 @@ namespace ImageSharp.Quantizers for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -279,7 +272,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); } output[(y * source.Width) + x] = pixelValue; @@ -288,7 +281,7 @@ namespace ImageSharp.Quantizers } /// - /// Gets the index index of the given color in the palette. + /// Gets the index of the given color in the palette. /// /// The red value. /// The green value. @@ -309,7 +302,7 @@ namespace ImageSharp.Quantizers /// The cube. /// The moment. /// The result. - private static float Volume(Box cube, long[] moment) + private static float Volume(ref Box cube, long[] moment) { return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] @@ -336,12 +329,12 @@ namespace ImageSharp.Quantizers /// The direction. /// The moment. /// The result. - private static long Bottom(Box cube, int direction, long[] moment) + private static long Bottom(ref Box cube, int direction, long[] moment) { switch (direction) { // Red - case 0: + case 3: return -moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] @@ -352,7 +345,7 @@ namespace ImageSharp.Quantizers + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; // Green - case 1: + case 2: return -moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] @@ -363,7 +356,7 @@ namespace ImageSharp.Quantizers + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; // Blue - case 2: + case 1: return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] @@ -374,7 +367,7 @@ namespace ImageSharp.Quantizers + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; // Alpha - case 3: + case 0: return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] @@ -397,12 +390,12 @@ namespace ImageSharp.Quantizers /// The position. /// The moment. /// The result. - private static long Top(Box cube, int direction, int position, long[] moment) + private static long Top(ref Box cube, int direction, int position, long[] moment) { switch (direction) { // Red - case 0: + case 3: return moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A1)] - moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A0)] - moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A1)] @@ -413,7 +406,7 @@ namespace ImageSharp.Quantizers - moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A0)]; // Green - case 1: + case 2: return moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A1)] @@ -424,7 +417,7 @@ namespace ImageSharp.Quantizers - moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A0)]; // Blue - case 2: + case 1: return moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A1)] - moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A0)] - moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A1)] @@ -435,7 +428,7 @@ namespace ImageSharp.Quantizers - moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A0)]; // Alpha - case 3: + case 0: return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, position)] - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, position)] - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, position)] @@ -561,12 +554,12 @@ namespace ImageSharp.Quantizers /// /// The cube. /// The . - private float Variance(Box cube) + private float Variance(ref Box cube) { - float dr = Volume(cube, this.vmr); - float dg = Volume(cube, this.vmg); - float db = Volume(cube, this.vmb); - float da = Volume(cube, this.vma); + float dr = Volume(ref cube, this.vmr); + float dg = Volume(ref cube, this.vmg); + float db = Volume(ref cube, this.vmb); + float da = Volume(ref cube, this.vma); float xx = this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] @@ -586,7 +579,8 @@ namespace ImageSharp.Quantizers - this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; - return xx - (((dr * dr) + (dg * dg) + (db * db) + (da * da)) / Volume(cube, this.vwt)); + var vector = new Vector4(dr, dg, db, da); + return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt)); } /// @@ -607,38 +601,33 @@ namespace ImageSharp.Quantizers /// The whole alpha. /// The whole weight. /// The . - private float Maximize(Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) + private float Maximize(ref Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) { - long baseR = Bottom(cube, direction, this.vmr); - long baseG = Bottom(cube, direction, this.vmg); - long baseB = Bottom(cube, direction, this.vmb); - long baseA = Bottom(cube, direction, this.vma); - long baseW = Bottom(cube, direction, this.vwt); + long baseR = Bottom(ref cube, direction, this.vmr); + long baseG = Bottom(ref cube, direction, this.vmg); + long baseB = Bottom(ref cube, direction, this.vmb); + long baseA = Bottom(ref cube, direction, this.vma); + long baseW = Bottom(ref cube, direction, this.vwt); float max = 0F; cut = -1; for (int i = first; i < last; i++) { - float halfR = baseR + Top(cube, direction, i, this.vmr); - float halfG = baseG + Top(cube, direction, i, this.vmg); - float halfB = baseB + Top(cube, direction, i, this.vmb); - float halfA = baseA + Top(cube, direction, i, this.vma); - float halfW = baseW + Top(cube, direction, i, this.vwt); - - float temp; + float halfR = baseR + Top(ref cube, direction, i, this.vmr); + float halfG = baseG + Top(ref cube, direction, i, this.vmg); + float halfB = baseB + Top(ref cube, direction, i, this.vmb); + float halfA = baseA + Top(ref cube, direction, i, this.vma); + float halfW = baseW + Top(ref cube, direction, i, this.vwt); if (MathF.Abs(halfW) < Constants.Epsilon) { continue; } - temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; + var vector = new Vector4(halfR, halfG, halfB, halfA); + float temp = Vector4.Dot(vector, vector) / halfW; - halfR = wholeR - halfR; - halfG = wholeG - halfG; - halfB = wholeB - halfB; - halfA = wholeA - halfA; halfW = wholeW - halfW; if (MathF.Abs(halfW) < Constants.Epsilon) @@ -646,7 +635,14 @@ namespace ImageSharp.Quantizers continue; } - temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; + halfR = wholeR - halfR; + halfG = wholeG - halfG; + halfB = wholeB - halfB; + halfA = wholeA - halfA; + + vector = new Vector4(halfR, halfG, halfB, halfA); + + temp += Vector4.Dot(vector, vector) / halfW; if (temp > max) { @@ -664,24 +660,24 @@ namespace ImageSharp.Quantizers /// The first set. /// The second set. /// Returns a value indicating whether the box has been split. - private bool Cut(Box set1, Box set2) + private bool Cut(ref Box set1, ref Box set2) { - float wholeR = Volume(set1, this.vmr); - float wholeG = Volume(set1, this.vmg); - float wholeB = Volume(set1, this.vmb); - float wholeA = Volume(set1, this.vma); - float wholeW = Volume(set1, this.vwt); + float wholeR = Volume(ref set1, this.vmr); + float wholeG = Volume(ref set1, this.vmg); + float wholeB = Volume(ref set1, this.vmb); + float wholeA = Volume(ref set1, this.vma); + float wholeW = Volume(ref set1, this.vwt); - float maxr = this.Maximize(set1, 0, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); - float maxg = this.Maximize(set1, 1, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); - float maxb = this.Maximize(set1, 2, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); - float maxa = this.Maximize(set1, 3, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxb = this.Maximize(ref set1, 1, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxa = this.Maximize(ref set1, 0, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); int dir; if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa)) { - dir = 0; + dir = 3; if (cutr < 0) { @@ -690,15 +686,15 @@ namespace ImageSharp.Quantizers } else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa)) { - dir = 1; + dir = 2; } else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa)) { - dir = 2; + dir = 1; } else { - dir = 3; + dir = 0; } set2.R1 = set1.R1; @@ -709,7 +705,7 @@ namespace ImageSharp.Quantizers switch (dir) { // Red - case 0: + case 3: set2.R0 = set1.R1 = cutr; set2.G0 = set1.G0; set2.B0 = set1.B0; @@ -717,7 +713,7 @@ namespace ImageSharp.Quantizers break; // Green - case 1: + case 2: set2.G0 = set1.G1 = cutg; set2.R0 = set1.R0; set2.B0 = set1.B0; @@ -725,7 +721,7 @@ namespace ImageSharp.Quantizers break; // Blue - case 2: + case 1: set2.B0 = set1.B1 = cutb; set2.R0 = set1.R0; set2.G0 = set1.G0; @@ -733,7 +729,7 @@ namespace ImageSharp.Quantizers break; // Alpha - case 3: + case 0: set2.A0 = set1.A1 = cuta; set2.R0 = set1.R0; set2.G0 = set1.G0; @@ -752,7 +748,7 @@ namespace ImageSharp.Quantizers /// /// The cube. /// A label. - private void Mark(Box cube, byte label) + private void Mark(ref Box cube, byte label) { for (int r = cube.R0 + 1; r <= cube.R1; r++) { @@ -777,23 +773,21 @@ namespace ImageSharp.Quantizers this.colorCube = new Box[this.colors]; float[] vv = new float[this.colors]; - for (int i = 0; i < this.colors; i++) - { - this.colorCube[i] = new Box(); - } - - this.colorCube[0].R0 = this.colorCube[0].G0 = this.colorCube[0].B0 = this.colorCube[0].A0 = 0; - this.colorCube[0].R1 = this.colorCube[0].G1 = this.colorCube[0].B1 = IndexCount - 1; - this.colorCube[0].A1 = IndexAlphaCount - 1; + ref var cube = ref this.colorCube[0]; + cube.R0 = cube.G0 = cube.B0 = cube.A0 = 0; + cube.R1 = cube.G1 = cube.B1 = IndexCount - 1; + cube.A1 = IndexAlphaCount - 1; int next = 0; for (int i = 1; i < this.colors; i++) { - if (this.Cut(this.colorCube[next], this.colorCube[i])) + ref var nextCube = ref this.colorCube[next]; + ref var currentCube = ref this.colorCube[i]; + if (this.Cut(ref nextCube, ref currentCube)) { - vv[next] = this.colorCube[next].Volume > 1 ? this.Variance(this.colorCube[next]) : 0F; - vv[i] = this.colorCube[i].Volume > 1 ? this.Variance(this.colorCube[i]) : 0F; + vv[next] = nextCube.Volume > 1 ? this.Variance(ref nextCube) : 0F; + vv[i] = currentCube.Volume > 1 ? this.Variance(ref currentCube) : 0F; } else { @@ -813,7 +807,7 @@ namespace ImageSharp.Quantizers } } - if (temp <= 0.0) + if (temp <= 0F) { this.colors = i + 1; break; @@ -833,18 +827,19 @@ namespace ImageSharp.Quantizers { if (this.Dither) { - // The colors have changed so we need to use Euclidean distance caclulation to find the closest value. + // The colors have changed so we need to use Euclidean distance calculation to find the closest value. // This palette can never be null here. return this.GetClosestPixel(pixel, this.palette, this.colorMap); } // Expected order r->g->b->a - pixel.ToXyzwBytes(this.rgbaBuffer, 0); + var rgba = default(Rgba32); + pixel.ToRgba32(ref rgba); - int r = this.rgbaBuffer[0] >> (8 - IndexBits); - int g = this.rgbaBuffer[1] >> (8 - IndexBits); - int b = this.rgbaBuffer[2] >> (8 - IndexBits); - int a = this.rgbaBuffer[3] >> (8 - IndexAlphaBits); + int r = rgba.R >> (8 - IndexBits); + int g = rgba.G >> (8 - IndexBits); + int b = rgba.B >> (8 - IndexBits); + int a = rgba.A >> (8 - IndexAlphaBits); return this.tag[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; } diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs index 46ed22e4f9..cf4077b709 100644 --- a/src/Shared/AssemblyInfo.Common.cs +++ b/src/Shared/AssemblyInfo.Common.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Reflection; using System.Resources; @@ -12,9 +10,9 @@ using System.Runtime.CompilerServices; // associated with an assembly. [assembly: AssemblyDescription("A cross-platform library for processing of image files; written in C#")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("James Jackson-South")] -[assembly: AssemblyProduct("ImageSharp")] -[assembly: AssemblyCopyright("Copyright (c) James Jackson-South and contributors.")] +[assembly: AssemblyCompany("Six Labors")] +[assembly: AssemblyProduct("SixLabors.ImageSharp")] +[assembly: AssemblyCopyright("Copyright (c) Six Labors and contributors.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] @@ -34,8 +32,12 @@ using System.Runtime.CompilerServices; [assembly: AssemblyInformationalVersion("1.0.0.0")] // Ensure the internals can be built and tested. -[assembly: InternalsVisibleTo("ImageSharp.Drawing")] +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] [assembly: InternalsVisibleTo("ImageSharp.Benchmarks")] -[assembly: InternalsVisibleTo("ImageSharp.Tests")] [assembly: InternalsVisibleTo("ImageSharp.Formats.Tiff.Tests")] -[assembly: InternalsVisibleTo("ImageSharp.Sandbox46")] +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Tests")] +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Sandbox46")] + +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKeyToken=null")] + diff --git a/src/Shared/stylecop.json b/src/Shared/stylecop.json deleted file mode 100644 index df8f120a5b..0000000000 --- a/src/Shared/stylecop.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "James Jackson-South", - "copyrightText": "Copyright (c) James Jackson-South and contributors.\nLicensed under the Apache License, Version 2.0." - } - } -} \ No newline at end of file diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 0000000000..485ab604a5 --- /dev/null +++ b/stylecop.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace", + "elementOrder": [ + "kind" + ] + }, + "documentationRules": { + "xmlHeader": false, + "documentInternalElements": false, + "copyrightText": "Copyright (c) Six Labors and contributors.\nLicensed under the Apache License, Version 2.0." + } + } +} \ No newline at end of file diff --git a/tests/CodeCoverage/CodeCoverage.cmd b/tests/CodeCoverage/CodeCoverage.cmd index 1e16d5c14a..622001fcf7 100644 --- a/tests/CodeCoverage/CodeCoverage.cmd +++ b/tests/CodeCoverage/CodeCoverage.cmd @@ -9,9 +9,10 @@ cd .. cd .. dotnet restore ImageSharp.sln -dotnet build ImageSharp.sln --no-incremental -c release /p:codecov=true +rem Clean the solution to force a rebuild with /p:codecov=true +dotnet clean ImageSharp.sln -c Release rem The -threshold options prevents this taking ages... -tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" +tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj -c Release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[SixLabors.ImageSharp*]*" if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs index 41574d109f..6db03a4486 100644 --- a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs @@ -1,6 +1,6 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; /// /// The image benchmark base class. diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs new file mode 100644 index 0000000000..83c2a2ee89 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -0,0 +1,65 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; + + [Config(typeof(Config.ShortClr))] + public abstract class PackFromVector4 + where TPixel : struct, IPixel + { + private Buffer source; + + private Buffer destination; + + [Params(16, 128, 512)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count); + } + + [GlobalCleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } + + [Benchmark(Baseline = true)] + public void PerElement() + { + Vector4[] s = this.source.Array; + TPixel[] d = this.destination.Array; + + for (int i = 0; i < this.Count; i++) + { + d[i].PackFromVector4(s[i]); + } + } + + [Benchmark] + public void CommonBulk() + { + new PixelOperations().PackFromVector4(this.source, this.destination, this.Count); + } + + [Benchmark] + public void OptimizedBulk() + { + PixelOperations.Instance.PackFromVector4(this.source, this.destination, this.Count); + } + } + + public class PackFromVector4_Rgba32 : PackFromVector4 + { + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index 78bdfd1599..b4f6ea9c06 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp; - using ImageSharp.Memory; + using SixLabors.ImageSharp; + using SixLabors.ImageSharp.Memory; /// /// Compares two implementation candidates for general BulkPixelOperations.ToVector4(): diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 7b151989ea..5c3648c2d8 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -1,10 +1,10 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class PackFromXyzw where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 9976faa8c3..2bf4e0da67 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -1,12 +1,12 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using System.Numerics; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class ToVector4 where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 061a5bd37b..2d624c19f1 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -1,10 +1,10 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class ToXyz where TPixel : struct, IPixel @@ -35,11 +35,16 @@ namespace ImageSharp.Benchmarks.Color.Bulk { TPixel[] s = this.source.Array; byte[] d = this.destination.Array; + var rgb = default(Rgb24); for (int i = 0; i < this.Count; i++) { TPixel c = s[i]; - c.ToXyzBytes(d, i * 4); + int i3 = i * 3; + c.ToRgb24(ref rgb); + d[i3] = rgb.R; + d[i3 + 1] = rgb.G; + d[i3 + 2] = rgb.B; } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index f4bcce1152..150b55aed0 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -4,12 +4,12 @@ using System.Linq; using System.Threading.Tasks; // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class ToXyzw where TPixel : struct, IPixel @@ -40,11 +40,17 @@ namespace ImageSharp.Benchmarks.Color.Bulk { TPixel[] s = this.source.Array; byte[] d = this.destination.Array; + var rgba = default(Rgba32); for (int i = 0; i < this.Count; i++) { TPixel c = s[i]; - c.ToXyzwBytes(d, i * 4); + int i4 = i * 4; + c.ToRgba32(ref rgba); + d[i4] = rgba.R; + d[i4 + 1] = rgba.G; + d[i4 + 2] = rgba.B; + d[i4 + 3] = rgba.A; } } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index a641baafe5..02017cbb7d 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -3,11 +3,11 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using SystemColor = System.Drawing.Color; diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs index b29fdc25b6..c5792f5476 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToCieLabConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs index 3e7d609727..7528f75f80 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToHunterLabConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index f472dd2923..a4da780908 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToLmsConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index eeea86c6eb..dab0e7a515 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToRgbConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs index 18c8cb3728..335ecf4789 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { public partial class RgbToYCbCr { diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs index 2efd9bc429..c4a77acc26 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs @@ -1,14 +1,16 @@ -namespace ImageSharp.Benchmarks -{ - using System; - using System.Buffers; - using System.Numerics; - using System.Runtime.CompilerServices; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using BenchmarkDotNet.Attributes; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +namespace SixLabors.ImageSharp.Benchmarks +{ public partial class RgbToYCbCr { private const int InputColorCount = 64; @@ -181,17 +183,6 @@ Vector3 vectorCb = VectorCb * vectorRgb; Vector3 vectorCr = VectorCr * vectorRgb; - // Should be better in theory, but came out to be worse: :( - // Vector3 c = new Vector3(0, 128, 128); - // Vector3 xx = new Vector3(vectorY.X, vectorCb.X, vectorCr.X); - // Vector3 yy = new Vector3(vectorY.Y, -vectorCb.Y, -vectorCr.Y); - // Vector3 zz = new Vector3(vectorY.Z, vectorCb.Z, -vectorCr.Z); - - // c += xx + yy + zz; - // *yPtr++ = c.X; - // *cbPtr++ = c.Y; - // *crPtr++ = c.Z; - *yPtr++ = vectorY.X + vectorY.Y + vectorY.Z; *cbPtr++ = 128 + (vectorCb.X - vectorCb.Y + vectorCb.Z); *crPtr++ = 128 + (vectorCr.X - vectorCr.Y - vectorCr.Z); @@ -373,4 +364,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index 21d040e5c1..f4e0fd65f6 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class RgbWorkingSpaceAdapt { diff --git a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs index dad32626d3..2e3307d298 100644 --- a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs +++ b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 8e278330bd..17ce3a07d4 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -5,7 +5,7 @@ using BenchmarkDotNet.Configs; -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Jobs; @@ -17,12 +17,12 @@ namespace ImageSharp.Benchmarks this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); } - public class Short : Config + public class ShortClr : Config { - public Short() + public ShortClr() { this.Add( - Job.Default.WithLaunchCount(1) + Job.Clr.WithLaunchCount(1) .WithWarmupCount(3) .WithTargetCount(3) ); diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index 66104944ea..ce1a88599a 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -12,7 +12,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class DrawBeziers : BenchmarkBase { @@ -47,7 +47,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.DrawBeziers( + image.Mutate(x => x.DrawBeziers( Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { @@ -55,7 +55,7 @@ namespace ImageSharp.Benchmarks new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 500) - }); + })); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index a6af5a9769..4f40c001d9 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -12,7 +12,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class DrawLines : BenchmarkBase { @@ -45,14 +45,14 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.DrawLines( + image.Mutate(x => x.DrawLines( Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(550, 50), new Vector2(200, 400) - }); + })); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 3abd3b889d..fd8e4ad285 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -13,7 +13,7 @@ namespace ImageSharp.Benchmarks using System.IO; using System.Numerics; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class DrawPolygon : BenchmarkBase { @@ -47,14 +47,14 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.DrawPolygon( + image.Mutate(x => x.DrawPolygon( Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(550, 50), new Vector2(200, 400) - }); + })); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs index b7d1b96b74..f948c4921f 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -13,7 +13,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class FillPolygon : BenchmarkBase { @@ -55,13 +55,13 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.FillPolygon( + image.Mutate(x => x.FillPolygon( Rgba32.HotPink, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(550, 50), new Vector2(200, 400) - }); + })); using (MemoryStream ms = new MemoryStream()) { @@ -75,9 +75,9 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Fill( + image.Mutate(x => x.Fill( Rgba32.HotPink, - this.shape); + this.shape)); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index 0738812a1c..b3890c101e 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -14,7 +14,7 @@ namespace ImageSharp.Benchmarks using System.Numerics; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class FillRectangle : BenchmarkBase { @@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140)); + image.Mutate(x => x.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140))); return new CoreSize(image.Width, image.Height); } @@ -50,13 +50,13 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.FillPolygon( + image.Mutate(x => x.FillPolygon( Rgba32.HotPink, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 10), new Vector2(200, 150), - new Vector2(10, 150) }); + new Vector2(10, 150) })); return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index aa97efe00b..46d02e4197 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -11,9 +11,9 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.Drawing.Brushes; + using SixLabors.ImageSharp.Drawing.Brushes; using CoreBrushes = ImageSharp.Drawing.Brushes.Brushes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class FillWithPattern { @@ -40,7 +40,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink)); + image.Mutate(x => x.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink))); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/General/Abs.cs b/tests/ImageSharp.Benchmarks/General/Abs.cs index b5b4494670..a67f3f1078 100644 --- a/tests/ImageSharp.Benchmarks/General/Abs.cs +++ b/tests/ImageSharp.Benchmarks/General/Abs.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index b7ac2cd867..02df1a19e2 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; public class Array2D { diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index dd0ebd413e..ac6b3f93c7 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -2,7 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; @@ -10,7 +10,7 @@ namespace ImageSharp.Benchmarks.General using BenchmarkDotNet.Attributes; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class ArrayCopy { [Params(10, 100, 1000, 10000)] diff --git a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs index 27341b146f..45a8519a9c 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; diff --git a/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs b/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs similarity index 84% rename from tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs rename to tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs index f998290340..bad87cc11a 100644 --- a/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs +++ b/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs @@ -1,19 +1,22 @@ -namespace ImageSharp.Benchmarks.General -{ - using System.Numerics; - using System.Runtime.CompilerServices; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using BenchmarkDotNet.Attributes; +using System.Numerics; +using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Benchmarks.General +{ /// /// The goal of this benchmark is to measure the following Jpeg-related scenario: /// - Take 2 blocks of float-s /// - Divide each float pair, round the result /// - Iterate through all rounded values as int-s /// - public unsafe class RoundSinglePrecisionBlocks + public unsafe class Block8x8F_DivideRound { private const int ExecutionCount = 5; // Added this to reduce the effect of copying the blocks private static readonly Vector4 MinusOne = new Vector4(-1); @@ -25,7 +28,7 @@ [GlobalSetup] public void Setup() { - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { this.inputDividend[i] = i*44.8f; this.inputDivisior[i] = 100 - i; @@ -42,18 +45,18 @@ float* pDividend = (float*)&b1; float* pDivisor = (float*)&b2; - int* result = stackalloc int[Block8x8F.ScalarCount]; + int* result = stackalloc int[Block8x8F.Size]; for (int cnt = 0; cnt < ExecutionCount; cnt++) { sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { int a = (int) pDividend[i]; int b = (int) pDivisor; result[i] = RationalRound(a, b); } - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += result[i]; } @@ -75,12 +78,12 @@ for (int cnt = 0; cnt < ExecutionCount; cnt++) { sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { double value = pDividend[i] / pDivisor[i]; pDividend[i] = (float) System.Math.Round(value); } - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += (int) pDividend[i]; } @@ -101,7 +104,7 @@ { sum = 0; DivideRoundAll(ref bDividend, ref bDivisor); - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += (int)pDividend[i]; } diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs new file mode 100644 index 0000000000..d101bf0509 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs @@ -0,0 +1,67 @@ +// ReSharper disable InconsistentNaming + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg.Common; + +namespace SixLabors.ImageSharp.Benchmarks.General +{ + public class Block8x8F_Round + { + private Block8x8F block = default(Block8x8F); + + [GlobalSetup] + public void Setup() + { + if (Vector.Count != 8) + { + throw new NotSupportedException("Vector.Count != 8"); + } + + for (int i = 0; i < Block8x8F.Size; i++) + { + this.block[i] = i * 44.8f; + } + } + + [Benchmark(Baseline = true)] + public void ScalarRound() + { + ref float b = ref Unsafe.As(ref this.block); + + for (int i = 0; i < Block8x8F.Size; i++) + { + ref float v = ref Unsafe.Add(ref b, i); + v = (float)Math.Round(v); + } + } + + [Benchmark] + public void SimdRound() + { + ref Block8x8F b = ref this.block; + + ref Vector row0 = ref Unsafe.As>(ref b.V0L); + row0 = SimdUtils.FastRound(row0); + ref Vector row1 = ref Unsafe.As>(ref b.V1L); + row1 = SimdUtils.FastRound(row1); + ref Vector row2 = ref Unsafe.As>(ref b.V2L); + row2 = SimdUtils.FastRound(row2); + ref Vector row3 = ref Unsafe.As>(ref b.V3L); + row3 = SimdUtils.FastRound(row3); + ref Vector row4 = ref Unsafe.As>(ref b.V4L); + row4 = SimdUtils.FastRound(row4); + ref Vector row5 = ref Unsafe.As>(ref b.V5L); + row5 = SimdUtils.FastRound(row5); + ref Vector row6 = ref Unsafe.As>(ref b.V6L); + row6 = SimdUtils.FastRound(row6); + ref Vector row7 = ref Unsafe.As>(ref b.V7L); + row7 = SimdUtils.FastRound(row7); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Clamp.cs b/tests/ImageSharp.Benchmarks/General/Clamp.cs index ae53de9d3a..ef6bc3c402 100644 --- a/tests/ImageSharp.Benchmarks/General/Clamp.cs +++ b/tests/ImageSharp.Benchmarks/General/Clamp.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index c3a309bb8a..0ac1413be0 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -1,12 +1,12 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; public unsafe class ClearBuffer { diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index cc8d99b418..48ee266fe0 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -1,11 +1,11 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; public class IterateArray { diff --git a/tests/ImageSharp.Benchmarks/General/Modulus.cs b/tests/ImageSharp.Benchmarks/General/Modulus.cs index aa3e461e1c..e6d5ccce62 100644 --- a/tests/ImageSharp.Benchmarks/General/Modulus.cs +++ b/tests/ImageSharp.Benchmarks/General/Modulus.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using BenchmarkDotNet.Attributes; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs index e096fd828c..a1db525a69 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -142,7 +142,7 @@ namespace ImageSharp.Benchmarks.General [Params(32)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.compatibleMemLayoutRunner = new ConversionRunner(this.Count); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs index 721ac121a3..5b059e2e65 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Numerics; using System.Runtime.CompilerServices; @@ -108,7 +108,7 @@ namespace ImageSharp.Benchmarks.General [Params(32)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.nonVectorRunner = new ConversionRunner(this.Count); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs index 0c9a8af3fd..782c792393 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -111,7 +111,7 @@ namespace ImageSharp.Benchmarks.General [Params(128)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index bd2bf599c4..0e21caffbc 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -1,11 +1,11 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; // Pixel indexing benchmarks compare different methods for getting/setting all pixel values in a subsegment of a single pixel row. public abstract unsafe class PixelIndexing diff --git a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs index 820789ee7c..ae11806f32 100644 --- a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs +++ b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs index b882403c85..d189411b5d 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs index caadaaa347..6378467478 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs index 6b3edb192b..49ada2f368 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs new file mode 100644 index 0000000000..b384295570 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs @@ -0,0 +1,69 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System; + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class DivFloat : SIMDBenchmarkBase.Divide + { + protected override float GetTestValue() => 42; + + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + } + + public class Divide : SIMDBenchmarkBase.Divide + { + protected override uint GetTestValue() => 42; + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + } + + public class DivInt32 : SIMDBenchmarkBase.Divide + { + protected override int GetTestValue() => 42; + + [Benchmark(Baseline = true)] + public void Standard() + { + int v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + } + + public class DivInt16 : SIMDBenchmarkBase.Divide + { + protected override short GetTestValue() => 42; + + protected override Vector GetTestVector() => new Vector(new short[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}); + + [Benchmark(Baseline = true)] + public void Standard() + { + short v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = (short)(this.input[i] / v); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index e5ae49b2a6..8c5f568181 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs index 532acc02d5..913d4ce3c7 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs new file mode 100644 index 0000000000..d1b70f21b3 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs @@ -0,0 +1,50 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + using BenchmarkDotNet.Attributes; + + public class MulUInt32 : SIMDBenchmarkBase.Multiply + { + protected override uint GetTestValue() => 42u; + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] * v; + } + } + } + + public class MulInt32 : SIMDBenchmarkBase.Multiply + { + [Benchmark(Baseline = true)] + public void Standard() + { + int v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] * v; + } + } + } + + public class MulInt16 : SIMDBenchmarkBase.Multiply + { + protected override short GetTestValue() => 42; + + protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); + + [Benchmark(Baseline = true)] + public void Standard() + { + short v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = (short)(this.input[i] * v); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs index 30443fad9d..f3853a8b1f 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; using System.Runtime.InteropServices; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs new file mode 100644 index 0000000000..76987dbd21 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs @@ -0,0 +1,70 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + using BenchmarkDotNet.Attributes; + + public abstract class SIMDBenchmarkBase + where T : struct + { + protected T[] input; + + protected T[] result; + + protected T testValue; + + protected Vector testVector; + + protected virtual T GetTestValue() => default(T); + + protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); + + + [Params(32)] + public int InputSize { get; set; } + + [GlobalSetup] + public virtual void Setup() + { + this.input = new T[this.InputSize]; + this.result = new T[this.InputSize]; + this.testValue = this.GetTestValue(); + this.testVector = this.GetTestVector(); + } + + public abstract class Multiply : SIMDBenchmarkBase + { + [Benchmark] + public void Simd() + { + Vector v = this.testVector; + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = Unsafe.As>(ref this.input[i]); + a = a * v; + Unsafe.As>(ref this.result[i]) = a; + } + } + } + + public abstract class Divide : SIMDBenchmarkBase + { + [Benchmark] + public void Simd() + { + Vector v = this.testVector; + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = Unsafe.As>(ref this.input[i]); + a = a / v; + Unsafe.As>(ref this.result[i]) = a; + } + } + } + + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 73fc886487..147f66f8f7 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -1,11 +1,11 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System; using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; /// /// This benchmark compares different methods for fetching memory data into diff --git a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs index 1d4ed11936..674aef84c1 100644 --- a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs +++ b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; - - using ImageSharp.Memory; + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Memory; public class CopyPixels : BenchmarkBase { @@ -103,8 +103,8 @@ namespace ImageSharp.Benchmarks.Image Configuration.Default.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); - Span targetRow = target.GetRowSpan(y); + Span sourceRow = source.Frames.RootFrame.GetPixelRowSpan(y); + Span targetRow = target.Frames.RootFrame.GetPixelRowSpan(y); for (int x = 0; x < source.Width; x++) { diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs index 6f3a38b7fd..142675fa7a 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs index ae1add172d..e295aee732 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.IO; using BenchmarkDotNet.Attributes; - using ImageSharp; + using SixLabors.ImageSharp; using SixLabors.Primitives; using CoreImage = ImageSharp.Image; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs index 3f908d3622..a65c9f9298 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs deleted file mode 100644 index ece93f912e..0000000000 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Benchmarks.Image -{ - using System.Drawing; - using System.IO; - - using BenchmarkDotNet.Attributes; - - using CoreImage = ImageSharp.Image; - - using CoreSize = SixLabors.Primitives.Size; - - public class DecodeJpeg : BenchmarkBase - { - private byte[] jpegBytes; - - [GlobalSetup] - public void ReadImages() - { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Jpg/Baseline/Calliphora.jpg"); - } - } - - [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] - public Size JpegSystemDrawing() - { - using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) - { - using (Image image = Image.FromStream(memoryStream)) - { - return image.Size; - } - } - } - - [Benchmark(Description = "ImageSharp Jpeg")] - public CoreSize JpegCore() - { - using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) - { - using (Image image = CoreImage.Load(memoryStream)) - { - return new CoreSize(image.Width, image.Height); - } - } - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs index be1633c044..eb13cf9290 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs @@ -3,36 +3,46 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; + using SixLabors.ImageSharp.Tests; + using CoreImage = ImageSharp.Image; using CoreSize = SixLabors.Primitives.Size; + [Config(typeof(Config.ShortClr))] public class DecodePng : BenchmarkBase { private byte[] pngBytes; + private string TestImageFullPath => Path.Combine( + TestEnvironment.InputImagesDirectoryFullPath, + this.TestImage); + + [Params(TestImages.Png.Splash)] + public string TestImage { get; set; } + [GlobalSetup] public void ReadImages() { if (this.pngBytes == null) { - this.pngBytes = File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Png/splash.png"); + this.pngBytes = File.ReadAllBytes(this.TestImageFullPath); } } [Benchmark(Baseline = true, Description = "System.Drawing Png")] public Size PngSystemDrawing() { - using (MemoryStream memoryStream = new MemoryStream(this.pngBytes)) + using (var memoryStream = new MemoryStream(this.pngBytes)) { - using (Image image = Image.FromStream(memoryStream)) + using (var image = Image.FromStream(memoryStream)) { return image.Size; } @@ -42,9 +52,9 @@ namespace ImageSharp.Benchmarks.Image [Benchmark(Description = "ImageSharp Png")] public CoreSize PngCore() { - using (MemoryStream memoryStream = new MemoryStream(this.pngBytes)) + using (var memoryStream = new MemoryStream(this.pngBytes)) { - using (Image image = CoreImage.Load(memoryStream)) + using (var image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs index 4941e17cba..86994f2d35 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs index 852e175725..c509d3555b 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs @@ -3,16 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Collections.Generic; using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Bmp; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs index e6600594f0..7a6ddd79d7 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs index 8d1d147466..571299812c 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Collections.Generic; using System.Drawing.Imaging; @@ -6,7 +6,8 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Gif; [Config(typeof(SingleRunConfig))] public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index 02e3211a76..70ea164d69 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -3,15 +3,16 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.IO; using BenchmarkDotNet.Attributes; - using ImageSharp; - using ImageSharp.Formats; - using ImageSharp.Quantizers; + using SixLabors.ImageSharp; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.Quantizers; using CoreImage = ImageSharp.Image; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 81bb235eea..b3c1e4ee95 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -3,16 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; using System.IO; using BenchmarkDotNet.Attributes; - - using ImageSharp.Formats; - using ImageSharp.Quantizers; + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.Quantizers; + using SixLabors.ImageSharp.Quantizers.Base; + using SixLabors.ImageSharp.Tests; using CoreImage = ImageSharp.Image; @@ -34,9 +35,11 @@ namespace ImageSharp.Benchmarks.Image { if (this.bmpStream == null) { - string path = this.LargeImage - ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg" - : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; + string path = Path.Combine( + TestEnvironment.InputImagesDirectoryFullPath, + this.LargeImage ? TestImages.Jpeg.Baseline.Jpeg420Exif : TestImages.Bmp.Car); + + this.bmpStream = File.OpenRead(path); this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; @@ -55,7 +58,7 @@ namespace ImageSharp.Benchmarks.Image [Benchmark(Baseline = true, Description = "System.Drawing Png")] public void PngSystemDrawing() { - using (MemoryStream memoryStream = new MemoryStream()) + using (var memoryStream = new MemoryStream()) { this.bmpDrawing.Save(memoryStream, ImageFormat.Png); } @@ -64,14 +67,14 @@ namespace ImageSharp.Benchmarks.Image [Benchmark(Description = "ImageSharp Png")] public void PngCore() { - using (MemoryStream memoryStream = new MemoryStream()) + using (var memoryStream = new MemoryStream()) { - Quantizer quantizer = this.UseOctreeQuantizer - ? (Quantizer) + QuantizerBase quantizer = this.UseOctreeQuantizer + ? (QuantizerBase) new OctreeQuantizer() : new PaletteQuantizer(); - PngEncoder options = new PngEncoder() { Quantizer = quantizer }; + var options = new PngEncoder { Quantizer = quantizer }; this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs index 28616213b2..f9469e5fea 100644 --- a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using SystemColor = System.Drawing.Color; diff --git a/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs b/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs index f68f4a418f..8b25ba6fed 100644 --- a/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs +++ b/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs @@ -12,7 +12,7 @@ #if TEST // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System; using System.Collections.Generic; diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs new file mode 100644 index 0000000000..6cb5f8a219 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpeg.cs @@ -0,0 +1,78 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg +{ + using System.Drawing; + using System.IO; + + using BenchmarkDotNet.Attributes; + + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.Tests; + + using CoreImage = SixLabors.ImageSharp.Image; + + using CoreSize = SixLabors.Primitives.Size; + + [Config(typeof(Config.ShortClr))] + public class DecodeJpeg : BenchmarkBase + { + private byte[] jpegBytes; + + private string TestImageFullPath => Path.Combine( + TestEnvironment.InputImagesDirectoryFullPath, + this.TestImage); + + [Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] + public string TestImage { get; set; } + + [GlobalSetup] + public void ReadImages() + { + if (this.jpegBytes == null) + { + this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); + } + } + + [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] + public Size JpegSystemDrawing() + { + using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) + { + using (Image image = Image.FromStream(memoryStream)) + { + return image.Size; + } + } + } + + [Benchmark(Description = "Decode Jpeg - ImageSharp")] + public CoreSize JpegImageSharpOrig() + { + using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) + { + using (Image image = CoreImage.Load(memoryStream, new OrigJpegDecoder())) + { + return new CoreSize(image.Width, image.Height); + } + } + } + + [Benchmark(Description = "Decode Jpeg - ImageSharp PdfJs")] + public CoreSize JpegImageSharpPdfJs() + { + using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) + { + using (Image image = CoreImage.Load(memoryStream, new PdfJsJpegDecoder())) + { + return new CoreSize(image.Width, image.Height); + } + } + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs similarity index 79% rename from tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs rename to tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs index 44c90d253d..bc60c56696 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/DecodeJpegMultiple.cs @@ -3,31 +3,33 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg { using System.Collections.Generic; + using BenchmarkDotNet.Attributes; - using CoreImage = ImageSharp.Image; + using CoreImage = SixLabors.ImageSharp.Image; - [Config(typeof(Config.Short))] + [Config(typeof(Config.ShortClr))] public class DecodeJpegMultiple : MultiImageBenchmarkBase { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { - "Jpg/" + "Jpg/baseline", + "Jpg/progressive", }; protected override IEnumerable SearchPatterns => new[] { "*.jpg" }; [Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] - public void DecodeJpegImageSharp() + public void DecodeJpegImageSharpNwq() { this.ForEachStream( ms => CoreImage.Load(ms) ); } - + [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] public void DecodeJpegSystemDrawing() { @@ -35,6 +37,5 @@ namespace ImageSharp.Benchmarks.Image System.Drawing.Image.FromStream ); } - } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpeg.cs similarity index 94% rename from tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs rename to tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpeg.cs index 07c2560fd8..15861bd617 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpeg.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg { using System.Drawing; using System.Drawing.Imaging; @@ -11,7 +11,7 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; - using CoreImage = ImageSharp.Image; + using CoreImage = SixLabors.ImageSharp.Image; public class EncodeJpeg : BenchmarkBase { diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs similarity index 87% rename from tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs rename to tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs index bbf838a9e1..4d28f5a198 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/EncodeJpegMultiple.cs @@ -3,17 +3,16 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg { using System.Collections.Generic; using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Jpeg; - - [Config(typeof(Config.Short))] // It's long enough to iterate through multiple files + [Config(typeof(Config.ShortClr))] // It's long enough to iterate through multiple files public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs new file mode 100644 index 0000000000..93420aacf8 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs @@ -0,0 +1,86 @@ +namespace SixLabors.ImageSharp.Benchmarks.Image.Jpeg +{ + using System; + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters; + using SixLabors.ImageSharp.Memory; + + [Config(typeof(Config.ShortClr))] + public class YCbCrColorConversion + { + private Buffer2D[] input; + + private Vector4[] output; + + public const int Count = 128; + + [GlobalSetup] + public void Setup() + { + this.input = CreateRandomValues(3, Count); + this.output = new Vector4[Count]; + } + + [GlobalCleanup] + public void Cleanup() + { + foreach (Buffer2D buffer in this.input) + { + buffer.Dispose(); + } + } + + [Benchmark(Baseline = true)] + public void Scalar() + { + var values = new JpegColorConverter.ComponentValues(this.input, 0); + + JpegColorConverter.FromYCbCrBasic.ConvertCore(values, this.output); + } + + [Benchmark] + public void SimdVector4() + { + var values = new JpegColorConverter.ComponentValues(this.input, 0); + + JpegColorConverter.FromYCbCrSimd.ConvertCore(values, this.output); + } + + [Benchmark] + public void SimdAvx2() + { + var values = new JpegColorConverter.ComponentValues(this.input, 0); + + JpegColorConverter.FromYCbCrSimdAvx2.ConvertCore(values, this.output); + } + + private static Buffer2D[] CreateRandomValues( + int componentCount, + int inputBufferLength, + float minVal = 0f, + float maxVal = 255f) + { + var rnd = new Random(42); + Buffer2D[] buffers = new Buffer2D[componentCount]; + for (int i = 0; i < componentCount; i++) + { + float[] values = new float[inputBufferLength]; + + for (int j = 0; j < inputBufferLength; j++) + { + values[j] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal; + } + + // no need to dispose when buffer is not array owner + buffers[i] = new Buffer2D(values, values.Length, 1); + } + + return buffers; + } + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs index e01a5951ef..2ed2d42c0d 100644 --- a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs @@ -4,7 +4,7 @@ // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System; using System.Collections.Generic; @@ -15,6 +15,8 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; + using SixLabors.ImageSharp.Tests; + using CoreImage = ImageSharp.Image; public abstract class MultiImageBenchmarkBase : BenchmarkBase @@ -39,14 +41,14 @@ namespace ImageSharp.Benchmarks.Image [Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)] public virtual InputImageCategory InputCategory { get; set; } - protected virtual string BaseFolder => "../ImageSharp.Tests/TestImages/Formats/"; + protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath; protected virtual IEnumerable SearchPatterns => new[] { "*.*" }; /// /// Gets the file names containing these strings are substrings are not processed by the benchmark. /// - protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof" }; + protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; /// /// Enumerates folders containing files OR files to be processed by the benchmark. diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 72593a0da4..417e849be1 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -3,20 +3,26 @@ netcoreapp1.1;net461 Exe True + SixLabors.ImageSharp.Benchmarks + ImageSharp.Benchmarks win7-x64 false + + + + - + - + - + diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 19904a5ad2..5a3131f796 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -3,17 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using CoreSize = SixLabors.Primitives.Size; using System.Numerics; - using ImageSharp.Memory; - using ImageSharp.PixelFormats.PixelBlenders; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats.PixelBlenders; public class PorterDuffBulkVsPixel : BenchmarkBase { diff --git a/tests/ImageSharp.Benchmarks/Program.cs b/tests/ImageSharp.Benchmarks/Program.cs index 789068426b..4dd63067ac 100644 --- a/tests/ImageSharp.Benchmarks/Program.cs +++ b/tests/ImageSharp.Benchmarks/Program.cs @@ -3,11 +3,11 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Running; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; using System.Reflection; public class Program diff --git a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs index cb13378a13..3681ff6f20 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using CoreSize = SixLabors.Primitives.Size; @@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Crop(100, 100); + image.Mutate(x => x.Crop(100, 100)); return new CoreSize(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index 1f69030a00..f65957128c 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.IO; using BenchmarkDotNet.Attributes; - using ImageSharp.Processing; + using SixLabors.ImageSharp.Processing; using CoreImage = ImageSharp.Image; @@ -38,17 +38,17 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp DetectEdges")] public void ImageProcessorCoreDetectEdges() { - this.image.DetectEdges(EdgeDetection.Kayyali); - this.image.DetectEdges(EdgeDetection.Kayyali); - this.image.DetectEdges(EdgeDetection.Kirsch); - this.image.DetectEdges(EdgeDetection.Lapacian3X3); - this.image.DetectEdges(EdgeDetection.Lapacian5X5); - this.image.DetectEdges(EdgeDetection.LaplacianOfGaussian); - this.image.DetectEdges(EdgeDetection.Prewitt); - this.image.DetectEdges(EdgeDetection.RobertsCross); - this.image.DetectEdges(EdgeDetection.Robinson); - this.image.DetectEdges(EdgeDetection.Scharr); - this.image.DetectEdges(EdgeDetection.Sobel); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Kayyali)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Kayyali)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Kirsch)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Lapacian3X3)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Lapacian5X5)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.LaplacianOfGaussian)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Prewitt)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.RobertsCross)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Robinson)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Scharr)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Sobel)); } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 2aa8df96ca..1f88c4fbfa 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -3,20 +3,22 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; - using ImageSharp.Processing.Processors; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing.Processors; using CoreSize = SixLabors.Primitives.Size; - using ImageSharp.Processing; + using SixLabors.ImageSharp.Processing; using System.Numerics; using System; using System.Threading.Tasks; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; using SixLabors.Primitives; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.Advanced; public class Glow : BenchmarkBase { @@ -26,7 +28,7 @@ namespace ImageSharp.Benchmarks [GlobalSetup] public void Setup() { - this.bulk = new GlowProcessor(NamedColors.Beige, GraphicsOptions.Default) { Radius = 800 * .5f, }; + this.bulk = new GlowProcessor(NamedColors.Beige, 800 * .5f, GraphicsOptions.Default); this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; } @@ -35,7 +37,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.ApplyProcessor(bulk, image.Bounds); + this.bulk.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); } } @@ -45,7 +47,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.ApplyProcessor(parallel, image.Bounds); + this.parallel.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); } } @@ -73,7 +75,7 @@ namespace ImageSharp.Benchmarks public float Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -81,7 +83,7 @@ namespace ImageSharp.Benchmarks int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle); - float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; + float maxDistance = this.Radius > 0 ? Math.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. int minX = Math.Max(0, startX); @@ -112,7 +114,7 @@ namespace ImageSharp.Benchmarks Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; @@ -134,13 +136,13 @@ namespace ImageSharp.Benchmarks amount = amount.Clamp(0, 1); // Santize on zero alpha - if (MathF.Abs(backdrop.W) < Constants.Epsilon) + if (Math.Abs(backdrop.W) < Constants.Epsilon) { source.W *= amount; return source; } - if (MathF.Abs(source.W) < Constants.Epsilon) + if (Math.Abs(source.W) < Constants.Epsilon) { return backdrop; } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 3873400698..6e7e2c8c48 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using CoreSize = SixLabors.Primitives.Size; @@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(2000, 2000)) { - image.Resize(400, 400); + image.Mutate(x => x.Resize(400, 400)); return new CoreSize(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index ae17f3698d..b186ff4df9 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -5,20 +5,22 @@ win7-x64 True false - ImageSharp.Sandbox46 + SixLabors.ImageSharp.Sandbox46 A cross-platform library for processing of image files written in C# Copyright © James Jackson-South and contributors. James Jackson-South and contributors James Jackson-South + SixLabors.ImageSharp.Sandbox46 + + + - - diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 6f93df16e5..f3875e24ad 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -3,15 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Sandbox46 +namespace SixLabors.ImageSharp.Sandbox46 { using System; using System.Runtime.DesignerServices; - using ImageSharp.Tests; - using ImageSharp.Tests.Colors; - using ImageSharp.Tests.PixelFormats; - using ImageSharp.Tests.Processing.Transforms; + using SixLabors.ImageSharp.Tests; + using SixLabors.ImageSharp.Tests.Colors; + using SixLabors.ImageSharp.Tests.Formats.Jpg; + using SixLabors.ImageSharp.Tests.PixelFormats; + using SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; + using SixLabors.ImageSharp.Tests.Processing.Transforms; using Xunit.Abstractions; @@ -39,14 +41,21 @@ namespace ImageSharp.Sandbox46 /// public static void Main(string[] args) { + RunJpegColorProfilingTests(); + // RunDecodeJpegProfilingTests(); // RunToVector4ProfilingTest(); - - RunResizeProfilingTest(); + // RunResizeProfilingTest(); Console.ReadLine(); } + private static void RunJpegColorProfilingTests() + { + new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(false); + new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(true); + } + private static void RunResizeProfilingTest() { ResizeProfilingBenchmarks test = new ResizeProfilingBenchmarks(new ConsoleOutput()); @@ -55,7 +64,7 @@ namespace ImageSharp.Sandbox46 private static void RunToVector4ProfilingTest() { - PixelOperationsTests.Color32 tests = new PixelOperationsTests.Color32(new ConsoleOutput()); + PixelOperationsTests.Rgba32 tests = new PixelOperationsTests.Rgba32(new ConsoleOutput()); tests.Benchmark_ToVector4(); } @@ -66,7 +75,7 @@ namespace ImageSharp.Sandbox46 foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) { string fileName = (string)data[0]; - benchmarks.DecodeJpeg(fileName); + benchmarks.DecodeJpeg_Original(fileName); } } } diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs new file mode 100644 index 0000000000..4291e775d4 --- /dev/null +++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using Xunit; +using SixLabors.ImageSharp.Advanced; +using System.Runtime.CompilerServices; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Advanced +{ + using SixLabors.ImageSharp.PixelFormats; + + public class AdvancedImageExtensionsTests + { + [Theory] + [WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)] + public unsafe void DangerousGetPinnableReference_CopyToBuffer(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + TPixel[] targetBuffer = new TPixel[image.Width * image.Height]; + + ref byte source = ref Unsafe.As(ref targetBuffer[0]); + ref byte dest = ref Unsafe.As(ref image.DangerousGetPinnableReferenceToPixelBuffer()); + + fixed (byte* targetPtr = &source) + fixed (byte* pixelBasePtr = &dest) + { + uint dataSizeInBytes = (uint)(image.Width * image.Height * Unsafe.SizeOf()); + Unsafe.CopyBlock(targetPtr, pixelBasePtr, dataSizeInBytes); + } + + image.ComparePixelBufferTo(targetBuffer); + } + } + } +} diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs new file mode 100644 index 0000000000..37696987cc --- /dev/null +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public abstract class BaseImageOperationsExtensionTest + { + protected readonly IImageProcessingContext operations; + private readonly FakeImageOperationsProvider.FakeImageOperations internalOperations; + protected readonly Rectangle rect; + protected readonly GraphicsOptions options; + + public BaseImageOperationsExtensionTest() + { + this.options = new GraphicsOptions(false) { }; + this.rect = new Rectangle(91, 123, 324, 56); // make this random? + this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(null, false); + this.operations = this.internalOperations; + } + + public T Verify(int index = 0) + { + Assert.InRange(index, 0, this.internalOperations.applied.Count - 1); + + var operation = this.internalOperations.applied[index]; + + return Assert.IsType(operation.Processor); + } + + public T Verify(Rectangle rect, int index = 0) + { + Assert.InRange(index, 0, this.internalOperations.applied.Count - 1); + + var operation = this.internalOperations.applied[index]; + + Assert.Equal(rect, operation.Rectangle); + return Assert.IsType(operation.Processor); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs index f2d8c92d28..4269459893 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// @@ -28,7 +30,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, 50, 180, 100, -50, 0)] [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] @@ -55,7 +56,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, -50, 0, 100, 50, 180)] [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs index d263bbe18e..17fd1db50f 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// @@ -28,7 +30,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, 50, 180, 100, -50, 0)] [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] @@ -55,7 +56,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, -50, 0, 100, 50, 180)] [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs index 9bd8b17c79..76d76f236c 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs index 5589e9753e..b18bd56dcb 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs index 9b441f4526..1652c53923 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs index 5ad224eaf0..1c48d00ff2 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs index 868d429757..f63c542122 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs index 02095b31f2..87dc59907b 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs @@ -1,13 +1,14 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; - using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests methods. /// Test data generated using: diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs index 2a5462b40f..f0ac56f4f1 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs @@ -1,39 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colorspaces -{ - using System; - using System.Numerics; - using Xunit; - - using ImageSharp.ColorSpaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Test implementations of IEquatable and IAlmostEquatable in our colorspaces /// public class ColorSpaceEqualityTests { - public static readonly TheoryData EmptyData = - new TheoryData + internal static readonly Dictionary EmptyDataLookup = + new Dictionary { - CieLab.Empty, - CieLch.Empty, - CieLchuv.Empty, - CieLuv.Empty, - CieXyz.Empty, - CieXyy.Empty, - Hsl.Empty, - Hsl.Empty, - HunterLab.Empty, - Lms.Empty, - LinearRgb.Empty, - Rgb.Empty, - YCbCr.Empty + {nameof( CieLab), CieLab.Empty }, + {nameof( CieLch), CieLch.Empty }, + {nameof( CieLchuv), CieLchuv.Empty }, + {nameof( CieLuv), CieLuv.Empty }, + {nameof( CieXyz), CieXyz.Empty }, + {nameof( CieXyy), CieXyy.Empty }, + {nameof( Hsl), Hsl.Empty }, + {nameof( HunterLab), HunterLab.Empty }, + {nameof( Lms), Lms.Empty }, + {nameof( LinearRgb), LinearRgb.Empty }, + {nameof( Rgb), Rgb.Empty }, + {nameof( YCbCr), YCbCr.Empty } }; + public static readonly IEnumerable EmptyData = EmptyDataLookup.Select(x => new [] { x.Key }); + public static readonly TheoryData EqualityData = new TheoryData { @@ -141,10 +142,11 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(EmptyData))] - public void Equality(IColorVector color) + public void Vector_Equals_WhenTrue(string color) { + IColorVector colorVector = EmptyDataLookup[color]; // Act - bool equal = color.Vector.Equals(Vector3.Zero); + bool equal = colorVector.Vector.Equals(Vector3.Zero); // Assert Assert.True(equal); @@ -152,7 +154,7 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(EqualityData))] - public void Equality(object first, object second, Type type) + public void Equals_WhenTrue(object first, object second, Type type) { // Act bool equal = first.Equals(second); @@ -165,7 +167,7 @@ namespace ImageSharp.Tests.Colorspaces [MemberData(nameof(NotEqualityDataNulls))] [MemberData(nameof(NotEqualityDataDifferentObjects))] [MemberData(nameof(NotEqualityData))] - public void NotEquality(object first, object second, Type type) + public void Equals_WhenFalse(object first, object second, Type type) { // Act bool equal = first.Equals(second); @@ -176,7 +178,7 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(EqualityData))] - public void HashCodeEqual(object first, object second, Type type) + public void GetHashCode_WhenEqual(object first, object second, Type type) { // Act bool equal = first.GetHashCode() == second.GetHashCode(); @@ -187,7 +189,7 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(NotEqualityDataDifferentObjects))] - public void HashCodeNotEqual(object first, object second, Type type) + public void GetHashCode_WhenNotEqual(object first, object second, Type type) { // Act bool equal = first.GetHashCode() == second.GetHashCode(); @@ -198,7 +200,7 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(EqualityData))] - public void EqualityObject(object first, object second, Type type) + public void GenericEquals_WhenTrue(object first, object second, Type type) { // Arrange // Cast to the known object types, this is so that we can hit the @@ -216,7 +218,7 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(NotEqualityData))] - public void NotEqualityObject(object first, object second, Type type) + public void GenericEquals_WhenFalse(object first, object second, Type type) { // Arrange // Cast to the known object types, this is so that we can hit the @@ -253,7 +255,7 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(NotEqualityData))] - public void NotEqualityOperator(object first, object second, Type type) + public void Operator_WhenTrue(object first, object second, Type type) { // Arrange // Cast to the known object types, this is so that we can hit the diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs index 7a4520a57e..ee71eefc17 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs index 8fe64e915b..6c3d579b4e 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs index fa02f887f2..a7071e883d 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs index f8d8c773a5..0dc58a0a3e 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs index 3f741ea3d9..0eb1f620bf 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Common/ConstantsTests.cs b/tests/ImageSharp.Tests/Common/ConstantsTests.cs index be4addf913..48ecbbbc91 100644 --- a/tests/ImageSharp.Tests/Common/ConstantsTests.cs +++ b/tests/ImageSharp.Tests/Common/ConstantsTests.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Common -{ - using Xunit; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Common +{ public class ConstantsTests { [Fact] diff --git a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs deleted file mode 100644 index 21e86d434c..0000000000 --- a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System.Linq; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - using Xunit; - - /// - /// Tests the class. - /// - public class PixelDataPoolTests - { - [Fact] - public void PixelDataPoolRentsMinimumSize() - { - Rgba32[] pixels = PixelDataPool.Rent(1024); - - Assert.True(pixels.Length >= 1024); - } - - [Fact] - public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() - { - Rgba32[] pixels = new Rgba32[1024]; - - PixelDataPool.Return(pixels); - - Assert.True(pixels.Length >= 1024); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void CalculateMaxArrayLength(bool isRawData) - { - int max = isRawData ? PixelDataPool.CalculateMaxArrayLength() - : PixelDataPool.CalculateMaxArrayLength(); - - Assert.Equal(max > 1024 * 1024, !isRawData); - } - - [Fact] - public void RentNonIPixelData() - { - byte[] data = PixelDataPool.Rent(16384); - - Assert.True(data.Length >= 16384); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs new file mode 100644 index 0000000000..8014925e23 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -0,0 +1,273 @@ +using System; +using System.Numerics; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Common +{ + using System.Linq; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Common.Tuples; + + using Xunit.Abstractions; + using Xunit.Sdk; + + public class SimdUtilsTests + { + private ITestOutputHelper Output { get; } + + public SimdUtilsTests(ITestOutputHelper output) + { + this.Output = output; + } + + private static int R(float f) => (int)Math.Round(f, MidpointRounding.AwayFromZero); + + private static int Re(float f) => (int)Math.Round(f, MidpointRounding.ToEven); + + // TODO: Move this to a proper test class! + [Theory] + [InlineData(0.32, 54.5, -3.5, -4.1)] + [InlineData(5.3, 536.4, 4.5, 8.1)] + public void PseudoRound(float x, float y, float z, float w) + { + var v = new Vector4(x, y, z, w); + + Vector4 actual = v.PseudoRound(); + + Assert.Equal( + R(v.X), + (int)actual.X + ); + Assert.Equal( + R(v.Y), + (int)actual.Y + ); + Assert.Equal( + R(v.Z), + (int)actual.Z + ); + Assert.Equal( + R(v.W), + (int)actual.W + ); + } + + private static Vector CreateExactTestVector1() + { + float[] data = new float[Vector.Count]; + + data[0] = 0.1f; + data[1] = 0.4f; + data[2] = 0.5f; + data[3] = 0.9f; + + for (int i = 4; i < Vector.Count; i++) + { + data[i] = data[i - 4] + 100f; + } + return new Vector(data); + } + + private static Vector CreateRandomTestVector(int seed, float min, float max) + { + float[] data = new float[Vector.Count]; + Random rnd = new Random(); + for (int i = 0; i < Vector.Count; i++) + { + float v = (float)rnd.NextDouble() * (max-min) + min; + data[i] = v; + } + return new Vector(data); + } + + [Fact] + public void FastRound() + { + Vector v = CreateExactTestVector1(); + Vector r = v.FastRound(); + + this.Output.WriteLine(r.ToString()); + + AssertEvenRoundIsCorrect(r, v); + } + + [Theory] + [InlineData(1, 1f)] + [InlineData(1, 10f)] + [InlineData(1, 1000f)] + [InlineData(42, 1f)] + [InlineData(42, 10f)] + [InlineData(42, 1000f)] + public void FastRound_RandomValues(int seed, float scale) + { + Vector v = CreateRandomTestVector(seed, -scale*0.5f, scale*0.5f); + Vector r = v.FastRound(); + + this.Output.WriteLine(v.ToString()); + this.Output.WriteLine(r.ToString()); + + AssertEvenRoundIsCorrect(r, v); + } + + private bool SkipOnNonAvx2([CallerMemberName] string testCaseName = null) + { + if (!SimdUtils.IsAvx2CompatibleArchitecture) + { + this.Output.WriteLine("Skipping AVX2 specific test case: " + testCaseName); + return true; + } + return false; + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 8)] + [InlineData(2, 16)] + [InlineData(3, 128)] + public void BulkConvertNormalizedFloatToByte_WithRoundedData(int seed, int count) + { + if (this.SkipOnNonAvx2()) + { + return; + } + + float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, 0, 256); + float[] normalized = orig.Select(f => f / 255f).ToArray(); + + byte[] dest = new byte[count]; + + SimdUtils.BulkConvertNormalizedFloatToByte(normalized, dest); + + byte[] expected = orig.Select(f => (byte)(f)).ToArray(); + + Assert.Equal(expected, dest); + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 8)] + [InlineData(2, 16)] + [InlineData(3, 128)] + public void BulkConvertNormalizedFloatToByte_WithNonRoundedData(int seed, int count) + { + if (this.SkipOnNonAvx2()) + { + return; + } + + float[] source = new Random(seed).GenerateRandomFloatArray(count, 0, 1f); + + byte[] dest = new byte[count]; + + SimdUtils.BulkConvertNormalizedFloatToByte(source, dest); + + byte[] expected = source.Select(f => (byte)Math.Round(f*255f)).ToArray(); + + Assert.Equal(expected, dest); + } + + private static float Clamp255(float x) => Math.Min(255f, Math.Max(0f, x)); + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 8)] + [InlineData(2, 16)] + [InlineData(3, 128)] + public void BulkConvertNormalizedFloatToByteClampOverflows(int seed, int count) + { + if (this.SkipOnNonAvx2()) + { + return; + } + + float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, -50, 444); + float[] normalized = orig.Select(f => f / 255f).ToArray(); + + byte[] dest = new byte[count]; + + SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(normalized, dest); + + byte[] expected = orig.Select(f => (byte)Clamp255(f)).ToArray(); + + Assert.Equal(expected, dest); + } + + [Theory] + [InlineData(0)] + [InlineData(7)] + [InlineData(42)] + [InlineData(255)] + [InlineData(256)] + [InlineData(257)] + private void MagicConvertToByte(float value) + { + byte actual = MagicConvert(value / 256f); + byte expected = (byte)value; + + Assert.Equal(expected, actual); + } + + [Fact] + private void BulkConvertNormalizedFloatToByte_Step() + { + if (this.SkipOnNonAvx2()) + { + return; + } + + float[] source = {0, 7, 42, 255, 0.5f, 1.1f, 2.6f, 16f}; + byte[] expected = source.Select(f => (byte)Math.Round(f)).ToArray(); + + source = source.Select(f => f / 255f).ToArray(); + + byte[] dest = new byte[8]; + + this.MagicConvert(source, dest); + + Assert.Equal(expected, dest); + } + + private static byte MagicConvert(float x) + { + float f = 32768.0f + x; + uint i = Unsafe.As(ref f); + return (byte)i; + } + + private void MagicConvert(Span source, Span dest) + { + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + Vector x = source.NonPortableCast>()[0]; + + x = (x * scale) + magick; + + Tuple8.OfUInt32 ii = default(Tuple8.OfUInt32); + + ref Vector iiRef = ref Unsafe.As>(ref ii); + + iiRef = x; + + //Tuple8.OfUInt32 ii = Unsafe.As, Tuple8.OfUInt32>(ref x); + + ref Tuple8.OfByte d = ref dest.NonPortableCast()[0]; + d.LoadFrom(ref ii); + + this.Output.WriteLine(ii.ToString()); + this.Output.WriteLine(d.ToString()); + } + + private static void AssertEvenRoundIsCorrect(Vector r, Vector v) + { + for (int i = 0; i < Vector.Count; i++) + { + int actual = (int)r[i]; + int expected = Re(v[i]); + Assert.Equal(expected, actual); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/StreamExtensionsTests.cs b/tests/ImageSharp.Tests/Common/StreamExtensionsTests.cs new file mode 100644 index 0000000000..8b2c65b07b --- /dev/null +++ b/tests/ImageSharp.Tests/Common/StreamExtensionsTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Common +{ + public class StreamExtensionsTests + { + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void Skip_CountZeroOrLower_PositionNotChanged(int count) + { + using (var memStream = new MemoryStream(5)) + { + memStream.Position = 4; + memStream.Skip(count); + + Assert.Equal(4, memStream.Position); + } + } + + [Fact] + public void Skip_SeekableStream_SeekIsCalled() + { + using (var seekableStream = new SeekableStream(4)) + { + seekableStream.Skip(4); + + Assert.Equal(4, seekableStream.Offset); + Assert.Equal(SeekOrigin.Current, seekableStream.Loc); + } + } + + [Fact] + public void Skip_NonSeekableStream_BytesAreRead() + { + using (var nonSeekableStream = new NonSeekableStream()) + { + nonSeekableStream.Skip(5); + + Assert.Equal(3, nonSeekableStream.Counts.Count); + + Assert.Equal(5, nonSeekableStream.Counts[0]); + Assert.Equal(3, nonSeekableStream.Counts[1]); + Assert.Equal(1, nonSeekableStream.Counts[2]); + } + } + + [Fact] + public void Skip_EofStream_NoExceptionIsThrown() + { + using (var eofStream = new EofStream(7)) + { + eofStream.Skip(7); + + Assert.Equal(0, eofStream.Position); + } + } + + private class SeekableStream : MemoryStream + { + public long Offset; + public SeekOrigin Loc; + + public SeekableStream(int capacity) : base(capacity) { } + + public override long Seek(long offset, SeekOrigin loc) + { + this.Offset = offset; + this.Loc = loc; + return base.Seek(offset, loc); + } + } + + private class NonSeekableStream : MemoryStream + { + public override bool CanSeek => false; + + public List Counts = new List(); + + public NonSeekableStream() : base(4) { } + + public override int Read(byte[] buffer, int offset, int count) + { + this.Counts.Add(count); + + return Math.Min(2, count); + } + } + + private class EofStream : MemoryStream + { + public override bool CanSeek => false; + + public EofStream(int capacity) : base(capacity) { } + + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + } + } +} diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 417588180a..18d4abdd1b 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - using Moq; - using Xunit; - /// /// Tests the configuration class. /// @@ -33,15 +30,15 @@ namespace ImageSharp.Tests [Fact] public void DefaultsToLocalFileSystem() { - Assert.IsType(DefaultConfiguration.FileSystem); - Assert.IsType(ConfigurationEmpty.FileSystem); + Assert.IsType(this.DefaultConfiguration.FileSystem); + Assert.IsType(this.ConfigurationEmpty.FileSystem); } [Fact] public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded() { - Assert.Equal(4, DefaultConfiguration.ImageEncoders.Count()); - Assert.Equal(4, DefaultConfiguration.ImageDecoders.Count()); + Assert.Equal(4, this.DefaultConfiguration.ImageEncoders.Count()); + Assert.Equal(4, this.DefaultConfiguration.ImageDecoders.Count()); } /// @@ -77,7 +74,7 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - DefaultConfiguration.AddImageFormatDetector(null); + this.DefaultConfiguration.AddImageFormatDetector(null); }); } @@ -86,15 +83,15 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - DefaultConfiguration.SetEncoder(null, new Mock().Object); + this.DefaultConfiguration.SetEncoder(null, new Mock().Object); }); Assert.Throws(() => { - DefaultConfiguration.SetEncoder(ImageFormats.Bitmap, null); + this.DefaultConfiguration.SetEncoder(ImageFormats.Bmp, null); }); Assert.Throws(() => { - DefaultConfiguration.SetEncoder(null, null); + this.DefaultConfiguration.SetEncoder(null, null); }); } @@ -103,29 +100,29 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - DefaultConfiguration.SetDecoder(null, new Mock().Object); + this.DefaultConfiguration.SetDecoder(null, new Mock().Object); }); Assert.Throws(() => { - DefaultConfiguration.SetDecoder(ImageFormats.Bitmap, null); + this.DefaultConfiguration.SetDecoder(ImageFormats.Bmp, null); }); Assert.Throws(() => { - DefaultConfiguration.SetDecoder(null, null); + this.DefaultConfiguration.SetDecoder(null, null); }); } [Fact] public void RegisterMimeTypeEncoderReplacesLast() { - var encoder1 = new Mock().Object; - ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - var found = ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); + IImageEncoder encoder1 = new Mock().Object; + this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); Assert.Equal(encoder1, found); - var encoder2 = new Mock().Object; - ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - var found2 = ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); + IImageEncoder encoder2 = new Mock().Object; + this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); Assert.Equal(encoder2, found2); Assert.NotEqual(found, found2); } @@ -133,14 +130,14 @@ namespace ImageSharp.Tests [Fact] public void RegisterMimeTypeDecoderReplacesLast() { - var decoder1 = new Mock().Object; - ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - var found = ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); + IImageDecoder decoder1 = new Mock().Object; + this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); Assert.Equal(decoder1, found); - var decoder2 = new Mock().Object; - ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - var found2 = ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); + IImageDecoder decoder2 = new Mock().Object; + this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); Assert.Equal(decoder2, found2); Assert.NotEqual(found, found2); } diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs index 7ddc2de5a4..5ffd9f5f14 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs @@ -1,38 +1,34 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using Drawing; - using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class Beziers : FileTestBase { [Fact] public void ImageShouldBeOverlayedByBezierLine() { - string path = this.CreateOutputDirectory("Drawing", "BezierLine"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); using (Image image = new Image(500, 500)) { - image.BackgroundColor(Rgba32.Blue) + image.Mutate(x => x.BackgroundColor(Rgba32.Blue) .DrawBeziers(Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[] { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - }) - .Save($"{path}/Simple.png"); + })); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -57,13 +53,13 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedBezierLineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "BezierLine"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); using (Image image = new Image(500, 500)) { - image.BackgroundColor(Rgba32.Blue) + image.Mutate(x => x.BackgroundColor(Rgba32.Blue) .DrawBeziers(color, 10, new SixLabors.Primitives.PointF[]{ @@ -71,8 +67,8 @@ namespace ImageSharp.Tests.Drawing new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - }) - .Save($"{path}/Opacity.png"); + })); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index 40a1cb3d3c..df029d2d72 100644 --- a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System; - using System.Linq; - using System.Collections.Generic; - using System.Text; - using ImageSharp.PixelFormats; - using Xunit; - using SixLabors.Primitives; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class BlendedShapes { public static IEnumerable modes = ((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))) @@ -27,11 +25,12 @@ namespace ImageSharp.Tests.Drawing { var scaleX = (img.Width / 100); var scaleY = (img.Height / 100); - img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)); - img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) - { - BlenderMode = mode - }); + img.Mutate(x=>x + .Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)) + .Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + })); img.DebugSave(provider, new { mode }); } } @@ -45,15 +44,15 @@ namespace ImageSharp.Tests.Drawing { var scaleX = (img.Width / 100); var scaleY = (img.Height / 100); - img.Fill(NamedColors.DarkBlue, new Rectangle(0* scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)); - img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + img.Mutate(x=>x.Fill(NamedColors.DarkBlue, new Rectangle(0* scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); + img.Mutate(x => x.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); - img.Fill(NamedColors.Transparent, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + })); + img.Mutate(x => x.Fill(NamedColors.Transparent, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); img.DebugSave(provider, new { mode }); } } @@ -67,20 +66,20 @@ namespace ImageSharp.Tests.Drawing { var scaleX = (img.Width / 100); var scaleY = (img.Height / 100); - img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40, 100 * scaleX, 20* scaleY)); - img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + img.Mutate(x=>x.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40, 100 * scaleX, 20* scaleY))); + img.Mutate(x => x.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); var c = NamedColors.Red.ToVector4(); c.W *= 0.5f; TPixel pixel = default(TPixel); pixel.PackFromVector4(c); - img.Fill(pixel, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + img.Mutate(x => x.Fill(pixel, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); img.DebugSave(provider, new { mode }); } } @@ -96,16 +95,11 @@ namespace ImageSharp.Tests.Drawing { var scaleX = (img.Width / 100); var scaleY = (img.Height / 100); - img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40* scaleY, 100 * scaleX, 20 * scaleY)); - //img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) - //{ - // BlenderMode = mode - //}); - - img.Fill(NamedColors.Black, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + img.Mutate(x => x.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40* scaleY, 100 * scaleX, 20 * scaleY))); + img.Mutate(x => x.Fill(NamedColors.Black, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); img.DebugSave(provider, new { mode }); } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 728ed749a2..6a55d8a561 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -1,15 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System.IO; - using System.Linq; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; + using System; + using System.Numerics; public class DrawImageTest : FileTestBase { @@ -40,8 +41,71 @@ namespace ImageSharp.Tests using (Image image = provider.GetImage()) using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { - image.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) - .DebugSave(provider, new { mode }); + image.Mutate(x => x.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))); + image.DebugSave(provider, new { mode }); + } + } + + [Theory] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)] + public void ImageShouldDrawTransformedImage(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) + { + Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(45F); + Matrix3x2 scale = Matrix3x2Extensions.CreateScale(new SizeF(.25F, .25F)); + + blend.Mutate(x => x.Transform(rotate * scale)); + + var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); + image.Mutate(x => x.DrawImage(blend, mode, .75F, new Size(blend.Width, blend.Height), position)); + image.DebugSave(provider, new[] { "Transformed" }); + } + } + + [Theory] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] + public void ImageShouldHandleNegativeLocation(TestImageProvider provider) + { + using (Image background = provider.GetImage()) + using (var overlay = new Image(50, 50)) + { + overlay.Mutate(x => x.Fill(Rgba32.Black)); + + int xy = -25; + Rgba32 backgroundPixel = background[0, 0]; + Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; + + background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Size(overlay.Width, overlay.Height), new Point(xy, xy))); + + Assert.Equal(Rgba32.White, backgroundPixel); + Assert.Equal(overlayPixel, background[0, 0]); + + background.DebugSave(provider, new[] { "Negative" }); + } + } + + [Theory] + [WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] + public void ImageShouldHandlePositiveLocation(TestImageProvider provider) + { + using (Image background = provider.GetImage()) + using (var overlay = new Image(50, 50)) + { + overlay.Mutate(x => x.Fill(Rgba32.Black)); + + int xy = 25; + Rgba32 backgroundPixel = background[xy - 1, xy - 1]; + Rgba32 overlayPixel = overlay[0, 0]; + + background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Size(overlay.Width, overlay.Height), new Point(xy, xy))); + + Assert.Equal(Rgba32.White, backgroundPixel); + Assert.Equal(overlayPixel, background[xy, xy]); + + background.DebugSave(provider, new[] { "Positive" }); } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index da5028f04f..429acafb95 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -1,26 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using ShapePath = SixLabors.Shapes.Path; - using SixLabors.Shapes; - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; - using ImageSharp.Drawing.Pens; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; +using ShapePath = SixLabors.Shapes.Path; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class DrawPathTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPath() { - string path = this.CreateOutputDirectory("Drawing", "Path"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); using (Image image = new Image(500, 500)) { LinearLineSegment linerSegemnt = new LinearLineSegment( @@ -34,10 +30,10 @@ namespace ImageSharp.Tests.Drawing ShapePath p = new ShapePath(linerSegemnt, bazierSegment); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, p) - .Save($"{path}/Simple.png"); + .Draw(Rgba32.HotPink, 5, p)); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -54,7 +50,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPathWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "Path"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); @@ -74,10 +70,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(color, 10, p) - .Save($"{path}/Opacity.png"); + .Draw(color, 10, p)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); @@ -98,15 +94,15 @@ namespace ImageSharp.Tests.Drawing public void PathExtendingOffEdgeOfImageShouldNotBeCropped() { - string path = this.CreateOutputDirectory("Drawing", "Path"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); using (var image = new Image(256, 256)) { - image.Fill(Rgba32.Black); + image.Mutate(x => x.Fill(Rgba32.Black)); var pen = Pens.Solid(Rgba32.White, 5f); for (int i = 0; i < 300; i += 20) { - image.DrawLines(pen, new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) }); + image.Mutate(x => x.DrawLines(pen, new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) })); } image diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs index bbabdf0ea6..d37058f5d1 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs @@ -1,29 +1,26 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System; - using System.IO; - - using ImageSharp.Drawing; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class FillPatternBrushTests : FileTestBase { private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) { - string path = this.CreateOutputDirectory("Fill", "PatternBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "PatternBrush"); using (Image image = new Image(20, 20)) { - image + image.Mutate(x => x .Fill(background) - .Fill(brush); + .Fill(brush)); image.Save($"{path}/{name}.png"); @@ -51,7 +48,8 @@ namespace ImageSharp.Tests.Drawing } } } - image.Resize(80, 80).Save($"{path}/{name}x4.png"); + image.Mutate(x => x.Resize(80, 80)); + image.Save($"{path}/{name}x4.png"); } } diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index b04a413857..db6c1157c4 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing -{ - using ImageSharp; - using Xunit; - using ImageSharp.Drawing; - using ImageSharp.Drawing.Processors; - using Moq; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using ImageSharp.PixelFormats; - using System; - using ImageSharp.Drawing.Pens; - using System.Numerics; +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; +using SixLabors.ImageSharp.Drawing.Brushes; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class FillRegionProcessorTests { [Theory] @@ -37,7 +39,7 @@ namespace ImageSharp.Tests.Drawing Image img = new Image(1, 1); processor.Apply(img, bounds); - region.Verify(x => x.Scan(It.IsAny(), It.IsAny>()), Times.Exactly(4)); + region.Verify(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(4)); } [Fact] @@ -51,7 +53,7 @@ namespace ImageSharp.Tests.Drawing region.Setup(x => x.Bounds).Returns(bounds); region.Setup(x => x.MaxIntersections).Returns(10); - region.Setup(x => x.Scan(It.IsAny(), It.IsAny>())) + region.Setup(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny())) .Returns>((y, span) => { if (y < 5) @@ -71,18 +73,16 @@ namespace ImageSharp.Tests.Drawing processor.Apply(img, bounds); } - - [Fact] public void DrawOffCanvas() { using (var img = new Image(10, 10)) { - img.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] { + img.Mutate(x => x.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] { new Vector2(-10, 5), new Vector2(20, 5), - }); + })); } } } diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index ce127cfe03..6eb139baca 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -1,31 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using Drawing; - using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class FillSolidBrushTests : FileTestBase { [Fact] public void ImageShouldBeFloodFilledWithColorOnDefaultBackground() { - string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush"); using (Image image = new Image(500, 500)) { + image.Mutate(x => x + .Fill(Rgba32.HotPink)); image - .Fill(Rgba32.HotPink) .Save($"{path}/DefaultBack.png"); using (PixelAccessor sourcePixels = image.Lock()) @@ -40,13 +37,13 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithColor() { - string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink) - .Save($"{path}/Simple.png"); + .Fill(Rgba32.HotPink)); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -60,15 +57,15 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithColorOpacity() { - string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush"); using (Image image = new Image(500, 500)) { Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(color) - .Save($"{path}/Opacity.png"); + .Fill(color)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index e058572fb3..6c0670a85e 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -1,26 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System.IO; - using Xunit; - using Drawing; - using ImageSharp.Drawing; - using System.Numerics; - using ImageSharp.Drawing.Pens; - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class LineComplexPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), @@ -34,10 +31,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) - .Save($"{path}/Simple.png"); + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -68,7 +65,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -81,10 +78,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) - .Save($"{path}/SimpleVanishHole.png"); + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Save($"{path}/SimpleVanishHole.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -116,7 +113,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineOverlapping() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -129,10 +126,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) - .Save($"{path}/SimpleOverlapping.png"); + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Save($"{path}/SimpleOverlapping.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -159,7 +156,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineDashed() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -172,10 +169,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1)) - .Save($"{path}/Dashed.png"); + .Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1))); + image.Save($"{path}/Dashed.png"); } } @@ -183,7 +180,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -197,10 +194,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(color, 5, simplePath.Clip(hole1)) - .Save($"{path}/Opacity.png"); + .Draw(color, 5, simplePath.Clip(hole1))); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs index f47d566967..d8c5c41d8b 100644 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineTests.cs @@ -1,37 +1,32 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using ImageSharp.Drawing; - using ImageSharp.Drawing.Pens; - - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class LineTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPath() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[]{ new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Simple.png"); + })); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -47,10 +42,10 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPath_NoAntialias() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[] { @@ -58,8 +53,8 @@ namespace ImageSharp.Tests.Drawing new Vector2(200, 150), new Vector2(50, 300) }, - new GraphicsOptions(false)) - .Save($"{path}/Simple_noantialias.png"); + new GraphicsOptions(false))); + image.Save($"{path}/Simple_noantialias.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -75,132 +70,132 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPathDashed() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.Dash(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Dashed.png"); - } + })); + image.Save($"{path}/Dashed.png"); } + } [Fact] public void ImageShouldBeOverlayedByPathDotted() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.Dot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Dot.png"); - } + })); + image.Save($"{path}/Dot.png"); } + } [Fact] public void ImageShouldBeOverlayedByPathDashDot() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.DashDot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/DashDot.png"); - } + })); + image.Save($"{path}/DashDot.png"); } + } [Fact] public void ImageShouldBeOverlayedByPathDashDotDot() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); Image image = new Image(500, 500); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/DashDotDot.png"); - } + })); + image.Save($"{path}/DashDotDot.png"); + } [Fact] public void ImageShouldBeOverlayedPathWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); Image image = new Image(500, 500); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(color, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Opacity.png"); + })); + image.Save($"{path}/Opacity.png"); - //shift background color towards forground color by the opacity amount - Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[11, 11]); - Assert.Equal(mergedColor, sourcePixels[199, 149]); + Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } + } [Fact] public void ImageShouldBeOverlayedByPathOutline() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); Image image = new Image(500, 500); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 10), new Vector2(200, 150), new Vector2(10, 150) - }) - .Save($"{path}/Rectangle.png"); + })); + image.Save($"{path}/Rectangle.png"); - using (PixelAccessor sourcePixels = image.Lock()) - { - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]); - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } - } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index 3a2b66c3c2..a88a5b09cd 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - - public class FillPath : IDisposable + public class FillPath : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; @@ -22,25 +24,12 @@ namespace ImageSharp.Tests.Drawing.Paths new Vector2(20,10), new Vector2(30,10), })); - private ProcessorWatchingImage img; - - public FillPath() - { - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } [Fact] public void CorrectlySetsBrushAndPath() { - img.Fill(brush, path); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, path); + var processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -56,10 +45,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - img.Fill(brush, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, path, noneDefault); + var processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); @@ -73,10 +60,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndPath() { - img.Fill(color, path); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(color, path); + var processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -91,10 +76,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - img.Fill(color, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(color, path, noneDefault); + var processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs index 24efa976e8..e8fad9d125 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - - public class FillPathCollection : IDisposable + public class FillPathCollection : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; @@ -30,29 +32,20 @@ namespace ImageSharp.Tests.Drawing.Paths })); IPathCollection pathCollection; - - private ProcessorWatchingImage img; - + public FillPathCollection() { this.pathCollection = new PathCollection(path1, path2); - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); } [Fact] public void CorrectlySetsBrushAndPath() { - img.Fill(brush, pathCollection); + this.operations.Fill(brush, pathCollection); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -69,12 +62,11 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - img.Fill(brush, pathCollection, noneDefault); + this.operations.Fill(brush, pathCollection, noneDefault); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(noneDefault, processor.Options); @@ -89,12 +81,11 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndPath() { - img.Fill(color, pathCollection); + this.operations.Fill(color, pathCollection); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -110,12 +101,11 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - img.Fill(color, pathCollection, noneDefault); + this.operations.Fill(color, pathCollection, noneDefault); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index b46d3d0d9c..b390c31069 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - - public class FillPolygon : IDisposable + public class FillPolygon : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; @@ -22,25 +24,14 @@ namespace ImageSharp.Tests.Drawing.Paths new Vector2(20,10), new Vector2(30,10), }; - private ProcessorWatchingImage img; - public FillPolygon() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } [Fact] public void CorrectlySetsBrushAndPath() { - img.FillPolygon(brush, path); + this.operations.FillPolygon(brush, path); - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -54,10 +45,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathAndOptions() { - img.FillPolygon(brush, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.FillPolygon(brush, path, noneDefault); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); @@ -71,10 +60,9 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndPath() { - img.FillPolygon(color, path); + this.operations.FillPolygon(color, path); + FillRegionProcessor processor = this.Verify>(); - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -89,10 +77,9 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - img.FillPolygon(color, path, noneDefault); + this.operations.FillPolygon(color, path, noneDefault); + FillRegionProcessor processor = this.Verify>(); - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index 7e6a3deee5..05e6bb29b3 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -1,41 +1,27 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - - using ImageSharp.Drawing.Brushes; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; - using ImageSharp.Drawing; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; - public class FillRectangle : IDisposable +namespace SixLabors.ImageSharp.Tests.Drawing.Paths +{ + public class FillRectangle : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); - private ProcessorWatchingImage img; - - public FillRectangle() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - [Fact] public void CorrectlySetsBrushAndRectangle() { - img.Fill(brush, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, rectangle); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -52,10 +38,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushRectangleAndOptions() { - img.Fill(brush, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, rectangle, noneDefault); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); @@ -72,11 +56,9 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndRectangle() { - img.Fill(color, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - + this.operations.Fill(color, rectangle); + FillRegionProcessor processor = this.Verify>(); + Assert.Equal(GraphicsOptions.Default, processor.Options); ShapeRegion region = Assert.IsType(processor.Region); @@ -93,10 +75,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorRectangleAndOptions() { - img.Fill(color, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(color, rectangle, noneDefault); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs deleted file mode 100644 index 1670b33520..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ /dev/null @@ -1,40 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Processing; - using System.Collections.Generic; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// Watches but does not actually run the processors against the image. - /// - /// - public class ProcessorWatchingImage : Image - { - public List ProcessorApplications { get; } = new List(); - - public ProcessorWatchingImage(int width, int height) - : base(Configuration.CreateDefaultInstance(), width, height) - { - } - - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - this.ProcessorApplications.Add(new ProcessorDetails - { - processor = processor, - rectangle = rectangle - }); - } - - public struct ProcessorDetails - { - public IImageProcessor processor; - public Rectangle rectangle; - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs index e791a2bfb7..5d2e93e877 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs @@ -1,21 +1,23 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using Moq; - using System.Collections.Immutable; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Processing; +using Moq; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing.Paths +{ public class ShapePathTests { // TODO readd these back in diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index df183a8aff..941807f542 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -1,22 +1,24 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Processing; +using Moq; +using SixLabors.Primitives; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using Moq; - using System.Collections.Immutable; - using SixLabors.Primitives; - public class ShapeRegionTests { private readonly Mock pathMock; @@ -74,17 +76,17 @@ namespace ImageSharp.Tests.Drawing.Paths int yToScan = 10; ShapeRegion region = new ShapeRegion(pathMock.Object); - pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((s, e, b) => { + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); Assert.True(s.X < bounds.Left); Assert.True(e.X > bounds.Right); }).Returns(0); - int i = region.Scan(yToScan, new float[0]); + int i = region.Scan(yToScan, new float[0], 0); - pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -93,17 +95,17 @@ namespace ImageSharp.Tests.Drawing.Paths int yToScan = 10; ShapeRegion region = new ShapeRegion(pathMock.Object); - pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((s, e, b) => { + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); Assert.True(s.X < bounds.Left); Assert.True(e.X > bounds.Right); }).Returns(0); - int i = region.Scan(yToScan, new float[0]); + int i = region.Scan(yToScan, new float[0], 0); - pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index 501b10e4af..a43f14eb71 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -1,39 +1,36 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing { - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using Xunit; - using Drawing; - using ImageSharp.Drawing; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - public class PolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = this.CreateOutputDirectory("Drawing", "Polygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .DrawPolygon(Rgba32.HotPink, 5, - new SixLabors.Primitives.PointF[] { + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .DrawPolygon(Rgba32.HotPink, 5, + new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Simple.png"); + })); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -51,7 +48,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "Polygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -62,10 +59,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .DrawPolygon(color, 10, simplePath) - .Save($"{path}/Opacity.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .DrawPolygon(color, 10, simplePath)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); @@ -86,14 +83,14 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByRectangleOutline() { - string path = this.CreateOutputDirectory("Drawing", "Polygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140)) - .Save($"{path}/Rectangle.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140))); + image.Save($"{path}/Rectangle.png"); using (PixelAccessor sourcePixels = image.Lock()) { diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs index 9c62e860a8..52668cc56c 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs @@ -1,25 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.Drawing.Brushes; - using System.IO; - using System.Linq; - - using ImageSharp.PixelFormats; - - using Xunit; - using SixLabors.Primitives; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class RecolorImageTest : FileTestBase { [Fact] public void ImageShouldRecolorYellowToHotPink() { - string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage"); RecolorBrush brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); @@ -27,8 +23,8 @@ namespace ImageSharp.Tests { using (Image image = file.CreateImage()) { - image.Fill(brush) - .Save($"{path}/{file.FileName}"); + image.Mutate(x => x.Fill(brush)); + image.Save($"{path}/{file.FileName}"); } } } @@ -36,7 +32,7 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldRecolorYellowToHotPinkInARectangle() { - string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage"); RecolorBrush brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); @@ -45,8 +41,8 @@ namespace ImageSharp.Tests using (Image image = file.CreateImage()) { int imageHeight = image.Height; - image.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2)) - .Save($"{path}/Shaped_{file.FileName}"); + image.Mutate(x => x.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2))); + image.Save($"{path}/Shaped_{file.FileName}"); } } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index ba904cb3f2..07e75acf43 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -1,25 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; - - using Xunit; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class SolidBezierTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByFilledPolygon() { - string path = this.CreateOutputDirectory("Drawing", "FilledBezier"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 400), new Vector2(30, 10), @@ -28,10 +23,10 @@ namespace ImageSharp.Tests.Drawing }; using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new Polygon(new CubicBezierLineSegment(simplePath))) - .Save($"{path}/Simple.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -49,7 +44,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonOpacity() { - string path = this.CreateOutputDirectory("Drawing", "FilledBezier"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 400), new Vector2(30, 10), @@ -60,10 +55,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .Fill(color, new Polygon(new CubicBezierLineSegment(simplePath))) - .Save($"{path}/Opacity.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .Fill(color, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index c3af3d5c28..e1849b0d01 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -1,26 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System.IO; - using Xunit; - using Drawing; - using ImageSharp.Drawing; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class SolidComplexPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -34,10 +30,10 @@ namespace ImageSharp.Tests.Drawing // var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20)); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, clipped) - .Save($"{path}/Simple.png"); + .Fill(Rgba32.HotPink, clipped)); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -53,7 +49,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOverlap() { - string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -66,10 +62,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, simplePath.Clip(hole1)) - .Save($"{path}/SimpleOverlapping.png"); + .Fill(Rgba32.HotPink, simplePath.Clip(hole1))); + image.Save($"{path}/SimpleOverlapping.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -84,7 +80,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -98,10 +94,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(color, simplePath.Clip(hole1)) - .Save($"{path}/Opacity.png"); + .Fill(color, simplePath.Clip(hole1))); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 793bcfc9f0..be7c8adb08 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -1,28 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing { - using Drawing; - using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Numerics; - using Xunit; - using ImageSharp.Drawing.Brushes; - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; - public class SolidPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByFilledPolygon() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -31,9 +28,9 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(true)) - .Save($"{path}/Simple.png"); + image.Mutate(x => x + .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(true))); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -45,7 +42,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonWithPattern() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -54,9 +51,9 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .FillPolygon(Brushes.Horizontal(Rgba32.HotPink), simplePath, new GraphicsOptions(true)) - .Save($"{path}/Pattern.png"); + image.Mutate(x => x + .FillPolygon(Brushes.Horizontal(Rgba32.HotPink), simplePath, new GraphicsOptions(true))); + image.Save($"{path}/Pattern.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -68,7 +65,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonNoAntialias() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -77,20 +74,20 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(false)) - .Save($"{path}/Simple_NoAntialias.png"); + .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(false))); + image.Save($"{path}/Simple_NoAntialias.png"); using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); + Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong"); - Assert.Equal(Rgba32.HotPink, sourcePixels[199, 150]); + Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong"); - Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); + Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong"); - Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); + Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong"); } } } @@ -98,7 +95,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonImage() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -110,17 +107,17 @@ namespace ImageSharp.Tests.Drawing { ImageBrush brush = new ImageBrush(brushImage); - image - .BackgroundColor(Rgba32.Blue) - .FillPolygon(brush, simplePath) - .Save($"{path}/Image.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .FillPolygon(brush, simplePath)); + image.Save($"{path}/Image.png"); } } [Fact] public void ImageShouldBeOverlayedByFilledPolygonOpacity() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -130,10 +127,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .FillPolygon(color, simplePath) - .Save($"{path}/Opacity.png"); + .FillPolygon(color, simplePath)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); @@ -148,14 +145,14 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledRectangle() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new SixLabors.Shapes.RectangularePolygon(10, 10, 190, 140)) - .Save($"{path}/Rectangle.png"); + .Fill(Rgba32.HotPink, new SixLabors.Shapes.RectangularePolygon(10, 10, 190, 140))); + image.Save($"{path}/Rectangle.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -175,14 +172,14 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledTriangle() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); using (Image image = new Image(100, 100)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30)) - .Save($"{path}/Triangle.png"); + .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30))); + image.Save($"{path}/Triangle.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -196,46 +193,46 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledSeptagon() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; using (Image image = new Image(config, 100, 100)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI)) - .Save($"{path}/Septagon.png"); + .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI))); + image.Save($"{path}/Septagon.png"); } } [Fact] public void ImageShouldBeOverlayedByFilledEllipse() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; using (Image image = new Image(config, 100, 100)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .Fill(Rgba32.HotPink, new EllipsePolygon(50, 50, 30, 50) - .Rotate((float)(Math.PI / 3))) - .Save($"{path}/ellipse.png"); + .Rotate((float)(Math.PI / 3)))); + image.Save($"{path}/ellipse.png"); } } [Fact] public void ImageShouldBeOverlayedBySquareWithCornerClipped() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; using (Image image = new Image(config, 200, 200)) { - image + image.Mutate(x => x .Fill(Rgba32.Blue) .FillPolygon(Rgba32.HotPink, new SixLabors.Primitives.PointF[] { @@ -245,8 +242,8 @@ namespace ImageSharp.Tests.Drawing new Vector2( 120, 64 ), new Vector2( 120, 120 ), new Vector2( 8, 120 ) - }) - .Save($"{path}/clipped-corner.png"); + })); + image.Save($"{path}/clipped-corner.png"); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs index 05a1f1e363..3f138248eb 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs @@ -1,26 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing.Text +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing.Paths; +using SixLabors.Fonts; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Text { - using System; - using System.Numerics; - - using ImageSharp.Drawing; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Drawing.Pens; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - using ImageSharp.Tests.Drawing.Paths; - - using SixLabors.Fonts; - using SixLabors.Shapes; - - using Xunit; - - public class DrawText_Path : IDisposable + public class DrawText_Path : BaseImageOperationsExtensionTest { Rgba32 color = Rgba32.HotPink; @@ -30,8 +25,6 @@ namespace ImageSharp.Tests.Drawing.Text new LinearLineSegment( new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(20, 10), new Vector2(20, 10), new Vector2(30, 10), })); - private ProcessorWatchingImage img; - private readonly FontCollection FontCollection; private readonly Font Font; @@ -40,18 +33,12 @@ namespace ImageSharp.Tests.Drawing.Text { this.FontCollection = new FontCollection(); this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")).CreateFont(12); - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - this.img.Dispose(); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPen() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -59,50 +46,49 @@ namespace ImageSharp.Tests.Drawing.Text path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, path); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSet() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenColorSet() { - this.img.DrawText("123", this.Font, Rgba32.Red, path, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Rgba32.Red, path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -111,13 +97,11 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void FillsForEachACharachterWhenColorSetDefaultOptions() { - this.img.DrawText("123", this.Font, Rgba32.Red, path); + this.operations.DrawText("123", this.Font, Rgba32.Red, path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -126,7 +110,7 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrush() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, null, @@ -134,45 +118,45 @@ namespace ImageSharp.Tests.Drawing.Text path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() { - this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), path); + this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSet() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetDefaultOptions() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -180,72 +164,49 @@ namespace ImageSharp.Tests.Drawing.Text path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void BrushAppliesBeforPen() { - this.img.DrawText( + this.operations.DrawText( "1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path, - new TextGraphicsOptions(true)); + new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); + var processor = this.Verify>(0); + this.Verify>(1); } [Fact] public void BrushAppliesBeforPenDefaultOptions() { - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); - } + this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); - [Fact] - public void GlyphHeightChangesBasedOnuseImageResolutionFlag() - { - this.img.MetaData.VerticalResolution = 1; - this.img.MetaData.HorizontalResolution = 1; - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true) { - UseImageResolution = false - }); - - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true) - { - UseImageResolution = true - }); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); - - ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); - ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); - - // magic numbers based on the font used at well known resolutions - Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2); - Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2); + var processor = this.Verify>(0); + this.Verify>(1); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 1c8c9f1d16..d9fe1a76db 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -1,26 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing.Text +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing.Paths; +using SixLabors.Fonts; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Text { - using System; - using System.Numerics; - - using ImageSharp.Drawing; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Drawing.Pens; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - using ImageSharp.Tests.Drawing.Paths; - - using SixLabors.Fonts; - using SixLabors.Shapes; - - using Xunit; - - public class DrawText : IDisposable + public class DrawText : BaseImageOperationsExtensionTest { Rgba32 color = Rgba32.HotPink; @@ -30,8 +25,6 @@ namespace ImageSharp.Tests.Drawing.Text new LinearLineSegment( new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(20, 10), new Vector2(20, 10), new Vector2(30, 10), })); - private ProcessorWatchingImage img; - private readonly FontCollection FontCollection; private readonly Font Font; @@ -40,18 +33,12 @@ namespace ImageSharp.Tests.Drawing.Text { this.FontCollection = new FontCollection(); this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")).CreateFont(12); - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - this.img.Dispose(); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPen() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -59,50 +46,49 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSet() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenColorSet() { - this.img.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -111,13 +97,11 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void FillsForEachACharachterWhenColorSetDefaultOptions() { - this.img.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero); + this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -126,7 +110,7 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrush() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, null, @@ -134,45 +118,45 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() { - this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSet() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetDefaultOptions() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -180,23 +164,32 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void BrushAppliesBeforPen() { - this.img.DrawText( + this.operations.DrawText( "1", this.Font, Brushes.Solid(Rgba32.Red), @@ -204,48 +197,17 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); + var processor = this.Verify>(0); + this.Verify>(1); } [Fact] public void BrushAppliesBeforPenDefaultOptions() { - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); - } + this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - [Fact] - public void GlyphHeightChangesBasedOnuseImageResolutionFlag() - { - this.img.MetaData.VerticalResolution = 1; - this.img.MetaData.HorizontalResolution = 1; - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true) { - UseImageResolution = false - }); - - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true) - { - UseImageResolution = true - }); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); - - ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); - ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); - - // magic numbers based on the font used at well known resolutions - Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2); - Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2); + var processor = this.Verify>(0); + this.Verify>(1); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs index ce2a5becf9..079510c33a 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs @@ -1,22 +1,23 @@ - -namespace ImageSharp.Tests.Drawing.Text -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using SixLabors.Fonts; +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Fonts; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing.Text +{ public class OutputText : FileTestBase { private readonly FontCollection FontCollection; @@ -34,9 +35,9 @@ namespace ImageSharp.Tests.Drawing.Text //draws 2 overlapping triangle glyphs twice 1 set on each line using (Image img = new Image(100, 200)) { - img.Fill(Rgba32.DarkBlue) - .DrawText("AB\nAB", new Font(this.Font, 50), Rgba32.Red, new Vector2(0, 0)); - img.Save($"{this.CreateOutputDirectory("Drawing", "Text")}/AB.png"); + img.Mutate(x => x.Fill(Rgba32.DarkBlue) + .DrawText("AB\nAB", new Font(this.Font, 50), Rgba32.Red, new Vector2(0, 0))); + img.Save($"{TestEnvironment.CreateOutputDirectory("Drawing", "Text")}/AB.png"); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs index 1a26bcfedc..975622e43d 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs @@ -1,15 +1,17 @@ - -namespace ImageSharp.Tests.Drawing.Text -{ - using ImageSharp.Drawing; - using SixLabors.Fonts; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Threading.Tasks; - using Xunit; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Drawing; +using SixLabors.Fonts; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing.Text +{ public class TextGraphicsOptionsTests { [Fact] diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs new file mode 100644 index 0000000000..f750bfcfad --- /dev/null +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests +{ + internal class FakeImageOperationsProvider : IImageProcessingContextFactory + { + private List ImageOperators = new List(); + + public bool HasCreated(Image source) + where TPixel : struct, IPixel + { + return Created(source).Any(); + } + public IEnumerable> Created(Image source) where TPixel : struct, IPixel + { + return this.ImageOperators.OfType>() + .Where(x => x.source == source); + } + + public IEnumerable.AppliedOpperation> AppliedOperations(Image source) where TPixel : struct, IPixel + { + return Created(source) + .SelectMany(x => x.applied); + } + + public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) where TPixel : struct, IPixel + { + var op = new FakeImageOperations(source, mutate); + this.ImageOperators.Add(op); + return op; + } + + + public class FakeImageOperations : IInternalImageProcessingContext + where TPixel : struct, IPixel + { + public Image source; + + public List applied = new List(); + public bool mutate; + + public FakeImageOperations(Image source, bool mutate) + { + this.mutate = mutate; + if (mutate) + { + this.source = source; + } + else + { + this.source = source?.Clone(); + } + } + + public Image Apply() + { + return source; + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + applied.Add(new AppliedOpperation + { + Processor = processor, + Rectangle = rectangle + }); + return this; + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + applied.Add(new AppliedOpperation + { + Processor = processor + }); + return this; + } + public struct AppliedOpperation + { + public Rectangle? Rectangle { get; set; } + public IImageProcessor Processor { get; set; } + } + } + } +} diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 51a1562f53..efd6c3b40e 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Tests +{ /// /// The test base class for reading and writing to files. /// - public abstract class FileTestBase : TestBase + public abstract class FileTestBase { /// + /// TODO: We really should not depend on this! Let's use well defined, test-case specific inputs everywhere! /// A collection made up of one file for each image format /// public static IEnumerable DefaultFiles = @@ -49,6 +48,11 @@ namespace ImageSharp.Tests /// public const PixelTypes DefaultPixelType = PixelTypes.Rgba32; + /// + /// A few other pixel types to prove that a processor is not bound to a single one. + /// + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + public static class Extensions { public const string Bmp = "bmp"; @@ -66,21 +70,25 @@ namespace ImageSharp.Tests protected static readonly List Files = new List { TestFile.Create(TestImages.Jpeg.Baseline.Calliphora), - // TestFile.Create(TestImages.Jpeg.Baseline.Turtle), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Baseline.Ycck), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Baseline.Cmyk), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Baseline.Floorplan), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Baseline.Bad.MissingEOF), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Progressive.Fb), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Progressive.Progress), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Baseline.GammaDalaiLamaGray), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Jpeg.Progressive.Bad.BadEOF), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.Turtle), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.Ycck), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.Cmyk), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.Floorplan), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Progressive.Festzug), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.Bad.BadEOF), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.Bad.ExifUndefType), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Progressive.Fb), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Progressive.Progress), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Baseline.GammaDalaiLamaGray), // Perf: Enable for local testing only + //TestFile.Create(TestImages.Jpeg.Progressive.Bad.BadEOF), // Perf: Enable for local testing only TestFile.Create(TestImages.Bmp.Car), // TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Bmp.CoreHeader), // Perf: Enable for local testing only TestFile.Create(TestImages.Png.Splash), + // TestFile.Create(TestImages.Png.SnakeGame), // TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only - // TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Png.Bad.ChunkLength1), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Png.Bad.ChunkLength2), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs new file mode 100644 index 0000000000..2c0121803b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests +{ + using System.IO; + + using SixLabors.ImageSharp.Formats.Bmp; + + public class BmpDecoderTests : FileTestBase + { + [Theory] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32)] + public void DecodeBmp(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider, "bmp"); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(TestImages.Bmp.F, CommonNonDefaultPixelTypes)] + public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider, "bmp"); + image.CompareToOriginal(provider); + } + } + + [Theory] + [InlineData(TestImages.Bmp.Car, 24)] + [InlineData(TestImages.Bmp.F, 24)] + [InlineData(TestImages.Bmp.NegHeight, 24)] + [InlineData(TestImages.Bmp.Bit8, 8)] + [InlineData(TestImages.Bmp.Bit8Inverted, 8)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index e5af28d8b4..d96d3def5e 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using ImageSharp.PixelFormats; - - using Xunit; - public class BmpEncoderTests : FileTestBase { public static readonly TheoryData BitsPerPixel @@ -24,7 +21,7 @@ namespace ImageSharp.Tests [MemberData(nameof(BitsPerPixel))] public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel) { - string path = this.CreateOutputDirectory("Bmp"); + string path = TestEnvironment.CreateOutputDirectory("Bmp"); foreach (TestFile file in Files) { diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 4ef8fe0612..97128e2c93 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -1,15 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System.IO; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; + using System; - using Xunit; + public class GeneralFormatTests : FileTestBase { @@ -22,14 +27,14 @@ namespace ImageSharp.Tests { image.MetaData.VerticalResolution = 150; image.MetaData.HorizontalResolution = 150; - image.DebugSave(provider, null, Extensions.Bmp); + image.DebugSave(provider); } } [Fact] public void ImageCanEncodeToString() { - string path = this.CreateOutputDirectory("ToString"); + string path = TestEnvironment.CreateOutputDirectory("ToString"); foreach (TestFile file in Files) { @@ -44,13 +49,13 @@ namespace ImageSharp.Tests [Fact] public void DecodeThenEncodeImageFromStreamShouldSucceed() { - string path = this.CreateOutputDirectory("Encode"); + string path = TestEnvironment.CreateOutputDirectory("Encode"); foreach (TestFile file in Files) { using (Image image = file.CreateImage()) { - image.Save($"{path}/{file.FileName}"); + image.Save($"{path}/{file.FileName}"); } } } @@ -58,37 +63,37 @@ namespace ImageSharp.Tests [Fact] public void QuantizeImageShouldPreserveMaximumColorPrecision() { - string path = this.CreateOutputDirectory("Quantize"); + string path = TestEnvironment.CreateOutputDirectory("Quantize"); foreach (TestFile file in Files) { using (Image srcImage = Image.Load(file.Bytes, out var mimeType)) { - using (Image image = new Image(srcImage)) + using (Image image = srcImage.Clone()) { using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) { - image.Quantize(Quantization.Octree) - .Save(output, mimeType); + image.Mutate(x => x.Quantize(Quantization.Octree)); + image.Save(output, mimeType); } } - using (Image image = new Image(srcImage)) + using (Image image = srcImage.Clone()) { using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) { - image.Quantize(Quantization.Wu) - .Save(output, mimeType); + image.Mutate(x => x.Quantize(Quantization.Wu)); + image.Save(output, mimeType); } } - using (Image image = new Image(srcImage)) + using (Image image = srcImage.Clone()) { using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) { - image.Quantize(Quantization.Palette) - .Save(output, mimeType); + image.Mutate(x => x.Quantize(Quantization.Palette)); + image.Save(output, mimeType); } } } @@ -98,7 +103,7 @@ namespace ImageSharp.Tests [Fact] public void ImageCanConvertFormat() { - string path = this.CreateOutputDirectory("Format"); + string path = TestEnvironment.CreateOutputDirectory("Format"); foreach (TestFile file in Files) { @@ -130,7 +135,7 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldPreservePixelByteOrderWhenSerialized() { - string path = this.CreateOutputDirectory("Serialized"); + string path = TestEnvironment.CreateOutputDirectory("Serialized"); foreach (TestFile file in Files) { @@ -149,5 +154,56 @@ namespace ImageSharp.Tests } } } + + [Theory] + [InlineData(10, 10, "png")] + [InlineData(100, 100, "png")] + [InlineData(100, 10, "png")] + [InlineData(10, 100, "png")] + [InlineData(10, 10, "gif")] + [InlineData(100, 100, "gif")] + [InlineData(100, 10, "gif")] + [InlineData(10, 100, "gif")] + [InlineData(10, 10, "bmp")] + [InlineData(100, 100, "bmp")] + [InlineData(100, 10, "bmp")] + [InlineData(10, 100, "bmp")] + [InlineData(10, 10, "jpg")] + [InlineData(100, 100, "jpg")] + [InlineData(100, 10, "jpg")] + [InlineData(10, 100, "jpg")] + public void CanIdentifyImageLoadedFromBytes(int width, int height, string format) + { + using (Image image = Image.LoadPixelData(new Rgba32[width * height], width, height)) + { + using (var memoryStream = new MemoryStream()) + { + image.Save(memoryStream, GetEncoder(format)); + memoryStream.Position = 0; + + var imageInfo = Image.Identify(memoryStream); + + Assert.Equal(imageInfo.Width, width); + Assert.Equal(imageInfo.Height, height); + } + } + } + + private static IImageEncoder GetEncoder(string format) + { + switch (format) + { + case "png": + return new PngEncoder(); + case "gif": + return new GifEncoder(); + case "bmp": + return new BmpEncoder(); + case "jpg": + return new JpegEncoder(); + default: + throw new ArgumentOutOfRangeException(nameof(format), format, null); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 06bfd8990d..9a095548a7 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -1,16 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Text; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Text; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.PixelFormats; + using System.IO; + using SixLabors.ImageSharp.Advanced; public class GifDecoderTests { @@ -18,6 +19,8 @@ namespace ImageSharp.Tests public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans }; + public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 }; + [Theory] [WithFileCollection(nameof(TestFiles), PixelTypes)] public void DecodeAndReSave(TestImageProvider imageProvider) @@ -29,16 +32,30 @@ namespace ImageSharp.Tests imageProvider.Utility.SaveTestOutputFile(image, "gif"); } } - + + [Theory] + [WithFileCollection(nameof(TestFiles), PixelTypes)] + public void DecodeResizeAndSave(TestImageProvider imageProvider) + where TPixel : struct, IPixel + { + using (Image image = imageProvider.GetImage()) + { + image.Mutate(x => x.Resize(new Size(image.Width / 2, image.Height / 2))); + + imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + imageProvider.Utility.SaveTestOutputFile(image, "gif"); + } + } + [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { - GifDecoder options = new GifDecoder() + var options = new GifDecoder { IgnoreMetadata = false }; - TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { @@ -51,12 +68,12 @@ namespace ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored() { - GifDecoder options = new GifDecoder() + var options = new GifDecoder { IgnoreMetadata = true }; - TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { @@ -67,12 +84,12 @@ namespace ImageSharp.Tests [Fact] public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() { - GifDecoder options = new GifDecoder() + var options = new GifDecoder { TextEncoding = Encoding.Unicode }; - TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { @@ -80,5 +97,79 @@ namespace ImageSharp.Tests Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); } } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void CanDecodeJustOneFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.First })) + { + Assert.Equal(1, image.Frames.Count); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void CanDecodeAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.All })) + { + Assert.True(image.Frames.Count > 1); + } + } + + [Theory] + [InlineData(TestImages.Gif.Cheers, 8)] + [InlineData(TestImages.Gif.Giphy, 8)] + [InlineData(TestImages.Gif.Rings, 8)] + [InlineData(TestImages.Gif.Trans, 8)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); + } + } + + [Fact] + public void CanDecodeIntermingledImages() + { + using (var kumin1 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) + using (var icon = Image.Load(TestFile.Create(TestImages.Png.Icon).Bytes)) + using (var kumin2 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes)) + { + for (int i = 0; i < kumin1.Frames.Count; i++) + { + ImageFrame first = kumin1.Frames[i]; + ImageFrame second = kumin2.Frames[i]; + first.ComparePixelBufferTo(second.GetPixelSpan()); + } + } + } + + [Theory] + [WithFileCollection(nameof(BadAppExtFiles), PixelTypes.Rgba32)] + public void DecodeBadApplicationExtensionLength(TestImageProvider imageProvider) + where TPixel : struct, IPixel + { + using (Image image = imageProvider.GetImage()) + { + imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + } + } + + [Theory] + [WithFile(TestImages.Gif.Issues.BadDescriptorWidth, PixelTypes.Rgba32)] + public void DecodeBadDescriptorDimensionsLength(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "bmp"); + } + } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index c365396863..a06e36e2a6 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class GifEncoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs new file mode 100644 index 0000000000..b98a6fe8e7 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/AdobeMarkerTests.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + + using Xunit; + + public class AdobeMarkerTests + { + // Taken from actual test image + private readonly byte[] bytes = { 0x41, 0x64, 0x6F, 0x62, 0x65, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x2 }; + + // Altered components + private readonly byte[] bytes2 = { 0x41, 0x64, 0x6F, 0x62, 0x65, 0x0, 0x64, 0x0, 0x0, 0x1, 0x1, 0x1 }; + + [Fact] + public void MarkerLengthIsCorrect() + { + Assert.Equal(12, AdobeMarker.Length); + } + + [Fact] + public void MarkerReturnsCorrectParsedValue() + { + bool isAdobe = AdobeMarker.TryParse(this.bytes, out var marker); + + Assert.True(isAdobe); + Assert.Equal(100, marker.DCTEncodeVersion); + Assert.Equal(0, marker.APP14Flags0); + Assert.Equal(0, marker.APP14Flags1); + Assert.Equal(OrigJpegConstants.Adobe.ColorTransformYcck, marker.ColorTransform); + } + + [Fact] + public void MarkerIgnoresIncorrectValue() + { + bool isAdobe = AdobeMarker.TryParse(new byte[] { 0, 0, 0, 0 }, out var marker); + + Assert.False(isAdobe); + Assert.Equal(default(AdobeMarker), marker); + } + + [Fact] + public void MarkerEqualityIsCorrect() + { + AdobeMarker.TryParse(this.bytes, out var marker); + AdobeMarker.TryParse(this.bytes, out var marker2); + + Assert.True(marker.Equals(marker2)); + } + + [Fact] + public void MarkerInEqualityIsCorrect() + { + AdobeMarker.TryParse(this.bytes, out var marker); + AdobeMarker.TryParse(this.bytes2, out var marker2); + + Assert.False(marker.Equals(marker2)); + } + + [Fact] + public void MarkerHashCodeIsReplicable() + { + AdobeMarker.TryParse(this.bytes, out var marker); + AdobeMarker.TryParse(this.bytes, out var marker2); + + Assert.True(marker.GetHashCode().Equals(marker2.GetHashCode())); + } + + [Fact] + public void MarkerHashCodeIsUnique() + { + AdobeMarker.TryParse(this.bytes, out var marker); + AdobeMarker.TryParse(this.bytes2, out var marker2); + + Assert.False(marker.GetHashCode().Equals(marker2.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs deleted file mode 100644 index dc6985dd34..0000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ImageSharp.Formats; -using Xunit; -using Xunit.Abstractions; -// ReSharper disable InconsistentNaming - -namespace ImageSharp.Tests -{ - using System.Numerics; - - using ImageSharp.Formats.Jpg; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - public class BadEOFJpegTests : MeasureFixture - { - public BadEOFJpegTests(ITestOutputHelper output) - : base(output) - { - } - - [Theory] - [WithFile(TestImages.Jpeg.Baseline.Bad.MissingEOF, PixelTypes.Rgba32)] - public void LoadBaselineImage(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Assert.NotNull(image); - provider.Utility.SaveTestOutputFile(image, "bmp"); - } - } - - [Theory] // TODO: #18 - [WithFile(TestImages.Jpeg.Progressive.Bad.BadEOF, PixelTypes.Rgba32)] - public void LoadProgressiveImage(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Assert.NotNull(image); - provider.Utility.SaveTestOutputFile(image, "bmp"); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs new file mode 100644 index 0000000000..191cfec731 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + + +// Uncomment this to turn unit tests into benchmarks: +//#define BENCHMARKING + +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + using SixLabors.Primitives; + + using Xunit; + using Xunit.Abstractions; + + public partial class Block8x8FTests : JpegFixture + { + public class CopyToBufferArea : JpegFixture + { + public CopyToBufferArea(ITestOutputHelper output) + : base(output) + { + } + + private static void VerifyAllZeroOutsideSubArea(Buffer2D buffer, int subX, int subY, int horizontalFactor = 1, int verticalFactor = 1) + { + for (int y = 0; y < 20; y++) + { + for (int x = 0; x < 20; x++) + { + if (x < subX || x >= subX + 8 * horizontalFactor || y < subY || y >= subY + 8 * verticalFactor) + { + Assert.Equal(0, buffer[x, y]); + } + } + } + } + + // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. + [Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] + //[Fact] + public void Unscaled() + { + Block8x8F block = CreateRandomFloatBlock(0, 100); + + using (var buffer = new Buffer2D(20, 20)) + { + BufferArea area = buffer.GetArea(5, 10, 8, 8); + block.CopyTo(area); + + Assert.Equal(block[0, 0], buffer[5, 10]); + Assert.Equal(block[1, 0], buffer[6, 10]); + Assert.Equal(block[0, 1], buffer[5, 11]); + Assert.Equal(block[0, 7], buffer[5, 17]); + Assert.Equal(block[63], buffer[12, 17]); + + VerifyAllZeroOutsideSubArea(buffer, 5, 10); + } + } + + // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. + [Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] + //[Theory] + [InlineData(1, 1)] + [InlineData(1, 2)] + [InlineData(2, 1)] + [InlineData(2, 2)] + [InlineData(4, 2)] + [InlineData(4, 4)] + public void Scaled(int horizontalFactor, int verticalFactor) + { + Block8x8F block = CreateRandomFloatBlock(0, 100); + + var start = new Point(50, 50); + + using (var buffer = new Buffer2D(100, 100)) + { + BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); + block.CopyTo(area, horizontalFactor, verticalFactor); + + for (int y = 0; y < 8 * verticalFactor; y++) + { + for (int x = 0; x < 8 * horizontalFactor; x++) + { + int yy = y / verticalFactor; + int xx = x / horizontalFactor; + + float expected = block[xx, yy]; + float actual = area[x, y]; + + Assert.Equal(expected, actual); + } + } + + VerifyAllZeroOutsideSubArea(buffer, start.X, start.Y, horizontalFactor, verticalFactor); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 01501a33d4..8c12b00505 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -1,25 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // Uncomment this to turn unit tests into benchmarks: //#define BENCHMARKING // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { + using System; using System.Diagnostics; - using System.Numerics; - using ImageSharp.Formats; - using ImageSharp.Formats.Jpg; + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using Xunit; using Xunit.Abstractions; - public class Block8x8FTests : JpegUtilityTestFixture + public partial class Block8x8FTests : JpegFixture { #if BENCHMARKING public const int Times = 1000000; @@ -32,54 +32,40 @@ namespace ImageSharp.Tests { } - [Fact] - public void Indexer() + private bool SkipOnNonAvx2Runner() { - float sum = 0; - this.Measure( - Times, - () => - { - Block8x8F block = new Block8x8F(); - - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - block[i] = i; - } - - sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - sum += block[i]; - } - }); - Assert.Equal(sum, 64f * 63f * 0.5f); + if (!SimdUtils.IsAvx2CompatibleArchitecture) + { + this.Output.WriteLine("AVX2 not supported, skipping!"); + return true; + } + return false; } [Fact] - public unsafe void Indexer_GetScalarAt_SetScalarAt() + public void Indexer() { float sum = 0; this.Measure( Times, () => { - Block8x8F block = new Block8x8F(); + var block = new Block8x8F(); - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { - Block8x8F.SetScalarAt(&block, i, i); + block[i] = i; } sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { - sum += Block8x8F.GetScalarAt(&block, i); + sum += block[i]; } }); Assert.Equal(sum, 64f * 63f * 0.5f); } - + [Fact] public void Indexer_ReferenceBenchmarkWithArray() { @@ -91,13 +77,13 @@ namespace ImageSharp.Tests { // Block8x8F block = new Block8x8F(); float[] block = new float[64]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { block[i] = i; } sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += block[i]; } @@ -108,10 +94,10 @@ namespace ImageSharp.Tests [Fact] public void Load_Store_FloatArray() { - float[] data = new float[Block8x8F.ScalarCount]; - float[] mirror = new float[Block8x8F.ScalarCount]; + float[] data = new float[Block8x8F.Size]; + float[] mirror = new float[Block8x8F.Size]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { data[i] = i; } @@ -120,23 +106,23 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F b = new Block8x8F(); + var b = new Block8x8F(); b.LoadFrom(data); b.CopyTo(mirror); }); Assert.Equal(data, mirror); - // PrintLinearData((MutableSpan)mirror); + // PrintLinearData((Span)mirror); } [Fact] public unsafe void Load_Store_FloatArray_Ptr() { - float[] data = new float[Block8x8F.ScalarCount]; - float[] mirror = new float[Block8x8F.ScalarCount]; + float[] data = new float[Block8x8F.Size]; + float[] mirror = new float[Block8x8F.Size]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { data[i] = i; } @@ -145,23 +131,23 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F b = new Block8x8F(); + var b = new Block8x8F(); Block8x8F.LoadFrom(&b, data); Block8x8F.CopyTo(&b, mirror); }); Assert.Equal(data, mirror); - // PrintLinearData((MutableSpan)mirror); + // PrintLinearData((Span)mirror); } [Fact] public void Load_Store_IntArray() { - int[] data = new int[Block8x8F.ScalarCount]; - int[] mirror = new int[Block8x8F.ScalarCount]; + int[] data = new int[Block8x8F.Size]; + int[] mirror = new int[Block8x8F.Size]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { data[i] = i; } @@ -170,14 +156,14 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F v = new Block8x8F(); + var v = new Block8x8F(); v.LoadFrom(data); v.CopyTo(mirror); }); Assert.Equal(data, mirror); - // PrintLinearData((MutableSpan)mirror); + // PrintLinearData((Span)mirror); } [Fact] @@ -186,10 +172,10 @@ namespace ImageSharp.Tests float[] expected = Create8x8FloatData(); ReferenceImplementations.Transpose8x8(expected); - Block8x8F source = new Block8x8F(); + var source = new Block8x8F(); source.LoadFrom(Create8x8FloatData()); - Block8x8F dest = new Block8x8F(); + var dest = new Block8x8F(); source.TransposeInto(ref dest); float[] actual = new float[64]; @@ -206,12 +192,12 @@ namespace ImageSharp.Tests [Fact] public void TranposeInto_Benchmark() { - BufferHolder source = new BufferHolder(); + var source = new BufferHolder(); source.Buffer.LoadFrom(Create8x8FloatData()); - BufferHolder dest = new BufferHolder(); + var dest = new BufferHolder(); this.Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {Times} ..."); - Stopwatch sw = Stopwatch.StartNew(); + var sw = Stopwatch.StartNew(); for (int i = 0; i < Times; i++) { @@ -221,115 +207,7 @@ namespace ImageSharp.Tests sw.Stop(); this.Output.WriteLine($"TranposeInto_PinningImpl_Benchmark finished in {sw.ElapsedMilliseconds} ms"); } - - [Fact] - public void iDCT2D8x4_LeftPart() - { - float[] sourceArray = Create8x8FloatData(); - float[] expectedDestArray = new float[64]; - - ReferenceImplementations.iDCT2D8x4_32f(sourceArray, expectedDestArray); - - Block8x8F source = new Block8x8F(); - source.LoadFrom(sourceArray); - - Block8x8F dest = new Block8x8F(); - - DCT.IDCT8x4_LeftPart(ref source, ref dest); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); - - Assert.Equal(expectedDestArray, actualDestArray); - } - - [Fact] - public void iDCT2D8x4_RightPart() - { - MutableSpan sourceArray = Create8x8FloatData(); - MutableSpan expectedDestArray = new float[64]; - - ReferenceImplementations.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); - - Block8x8F source = new Block8x8F(); - source.LoadFrom(sourceArray); - - Block8x8F dest = new Block8x8F(); - - DCT.IDCT8x4_RightPart(ref source, ref dest); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); - - Assert.Equal(expectedDestArray.Data, actualDestArray); - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - public void TransformIDCT(int seed) - { - MutableSpan sourceArray = Create8x8RandomFloatData(-200, 200, seed); - float[] expectedDestArray = new float[64]; - float[] tempArray = new float[64]; - - ReferenceImplementations.iDCT2D_llm(sourceArray, expectedDestArray, tempArray); - - // ReferenceImplementations.iDCT8x8_llm_sse(sourceArray, expectedDestArray, tempArray); - Block8x8F source = new Block8x8F(); - source.LoadFrom(sourceArray); - - Block8x8F dest = new Block8x8F(); - Block8x8F tempBuffer = new Block8x8F(); - - DCT.TransformIDCT(ref source, ref dest, ref tempBuffer); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); - Assert.Equal(expectedDestArray, actualDestArray, new ApproximateFloatComparer(1f)); - Assert.Equal(expectedDestArray, actualDestArray, new ApproximateFloatComparer(1f)); - } - - [Fact] - public unsafe void CopyColorsTo() - { - float[] data = Create8x8FloatData(); - Block8x8F block = new Block8x8F(); - block.LoadFrom(data); - block.MultiplyAllInplace(5); - - int stride = 256; - int height = 42; - int offset = height * 10 + 20; - - byte[] colorsExpected = new byte[stride * height]; - byte[] colorsActual = new byte[stride * height]; - - Block8x8F temp = new Block8x8F(); - - ReferenceImplementations.CopyColorsTo(ref block, new MutableSpan(colorsExpected, offset), stride); - - block.CopyColorsTo(new MutableSpan(colorsActual, offset), stride, &temp); - - // Output.WriteLine("******* EXPECTED: *********"); - // PrintLinearData(colorsExpected); - // Output.WriteLine("******** ACTUAL: **********"); - Assert.Equal(colorsExpected, colorsActual); - } - + private static float[] Create8x8ColorCropTestData() { float[] result = new float[64]; @@ -345,17 +223,17 @@ namespace ImageSharp.Tests } [Fact] - public void TransformByteConvetibleColorValuesInto() + public void NormalizeColors() { - Block8x8F block = new Block8x8F(); + var block = default(Block8x8F); float[] input = Create8x8ColorCropTestData(); block.LoadFrom(input); this.Output.WriteLine("Input:"); this.PrintLinearData(input); - Block8x8F dest = new Block8x8F(); - block.TransformByteConvetibleColorValuesInto(ref dest); - + Block8x8F dest = block; + dest.NormalizeColorsInplace(); + float[] array = new float[64]; dest.CopyTo(array); this.Output.WriteLine("Result:"); @@ -369,97 +247,170 @@ namespace ImageSharp.Tests [Theory] [InlineData(1)] [InlineData(2)] - public void FDCT8x4_LeftPart(int seed) + public void NormalizeColorsAndRoundAvx2(int seed) { - MutableSpan src = Create8x8RandomFloatData(-200, 200, seed); - Block8x8F srcBlock = new Block8x8F(); - srcBlock.LoadFrom(src); - - Block8x8F destBlock = new Block8x8F(); + if (this.SkipOnNonAvx2Runner()) + { + return; + } - MutableSpan expectedDest = new MutableSpan(64); + Block8x8F source = CreateRandomFloatBlock(-200, 200, seed); - ReferenceImplementations.fDCT2D8x4_32f(src, expectedDest); - DCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); + Block8x8F expected = source; + expected.NormalizeColorsInplace(); + expected.RoundInplace(); - MutableSpan actualDest = new MutableSpan(64); - destBlock.CopyTo(actualDest); + Block8x8F actual = source; + actual.NormalizeColorsAndRoundInplaceAvx2(); - Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); + this.Output.WriteLine(expected.ToString()); + this.Output.WriteLine(actual.ToString()); + this.CompareBlocks(expected, actual, 0); } + [Theory] [InlineData(1)] [InlineData(2)] - public void FDCT8x4_RightPart(int seed) + public unsafe void Quantize(int seed) { - MutableSpan src = Create8x8RandomFloatData(-200, 200, seed); - Block8x8F srcBlock = new Block8x8F(); - srcBlock.LoadFrom(src); + var block = new Block8x8F(); + block.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); - Block8x8F destBlock = new Block8x8F(); + var qt = new Block8x8F(); + qt.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); - MutableSpan expectedDest = new MutableSpan(64); + var unzig = ZigZag.CreateUnzigTable(); - ReferenceImplementations.fDCT2D8x4_32f(src.Slice(4), expectedDest.Slice(4)); - DCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); + int* expectedResults = stackalloc int[Block8x8F.Size]; + ReferenceImplementations.QuantizeRational(&block, expectedResults, &qt, unzig.Data); - MutableSpan actualDest = new MutableSpan(64); - destBlock.CopyTo(actualDest); + var actualResults = default(Block8x8F); - Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); + Block8x8F.Quantize(&block, &actualResults, &qt, unzig.Data); + + for (int i = 0; i < Block8x8F.Size; i++) + { + int expected = expectedResults[i]; + int actual = (int)actualResults[i]; + + Assert.Equal(expected, actual); + } + } + + [Fact] + public void RoundInto() + { + float[] data = Create8x8RandomFloatData(-1000, 1000); + + var source = default(Block8x8F); + source.LoadFrom(data); + var dest = default(Block8x8); + + source.RoundInto(ref dest); + + for (int i = 0; i < Block8x8.Size; i++) + { + float expectedFloat = data[i]; + short expectedShort = (short) Math.Round(expectedFloat); + short actualShort = dest[i]; + + Assert.Equal(expectedShort, actualShort); + } } [Theory] [InlineData(1)] [InlineData(2)] - public void TransformFDCT(int seed) + [InlineData(3)] + public void RoundInplaceSlow(int seed) { - MutableSpan src = Create8x8RandomFloatData(-200, 200, seed); - Block8x8F srcBlock = new Block8x8F(); - srcBlock.LoadFrom(src); + Block8x8F s = CreateRandomFloatBlock(-500, 500, seed); - Block8x8F destBlock = new Block8x8F(); + Block8x8F d = s; + d.RoundInplace(); - MutableSpan expectedDest = new MutableSpan(64); - MutableSpan temp1 = new MutableSpan(64); - Block8x8F temp2 = new Block8x8F(); + this.Output.WriteLine(s.ToString()); + this.Output.WriteLine(d.ToString()); - ReferenceImplementations.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); - DCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); + for (int i = 0; i < 64; i++) + { + float expected = (float)Math.Round(s[i]); + float actual = d[i]; + + Assert.Equal(expected, actual); + } + } + + [Fact] + public void MultiplyInplace_ByOtherBlock() + { + Block8x8F original = CreateRandomFloatBlock(-500, 500, 42); + Block8x8F m = CreateRandomFloatBlock(-500, 500, 42); - MutableSpan actualDest = new MutableSpan(64); - destBlock.CopyTo(actualDest); + Block8x8F actual = original; + + actual.MultiplyInplace(ref m); - Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); + for (int i = 0; i < Block8x8F.Size; i++) + { + Assert.Equal(original[i]*m[i], actual[i]); + } } [Theory] [InlineData(1)] [InlineData(2)] - public unsafe void UnzigDivRound(int seed) + [InlineData(3)] + public unsafe void DequantizeBlock(int seed) { - Block8x8F block = new Block8x8F(); - block.LoadFrom(Create8x8RandomFloatData(-2000, 2000, seed)); + Block8x8F original = CreateRandomFloatBlock(-500, 500, seed); + Block8x8F qt = CreateRandomFloatBlock(0, 10, seed + 42); - Block8x8F qt = new Block8x8F(); - qt.LoadFrom(Create8x8RandomFloatData(-2000, 2000, seed)); + var unzig = ZigZag.CreateUnzigTable(); - UnzigData unzig = UnzigData.Create(); + Block8x8F expected = original; + Block8x8F actual = original; - int* expectedResults = stackalloc int[Block8x8F.ScalarCount]; - ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data); + ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data); + Block8x8F.DequantizeBlock(&actual, &qt, unzig.Data); - Block8x8F actualResults = default(Block8x8F); + this.CompareBlocks(expected, actual, 0); + } - Block8x8F.UnzigDivRound(&block, &actualResults, &qt, unzig.Data); + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public unsafe void ZigZag_CreateDequantizationTable_MultiplicationShouldQuantize(int seed) + { + Block8x8F original = CreateRandomFloatBlock(-500, 500, seed); + Block8x8F qt = CreateRandomFloatBlock(0, 10, seed + 42); - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - int expected = expectedResults[i]; - int actual = (int)actualResults[i]; + var unzig = ZigZag.CreateUnzigTable(); + Block8x8F zigQt = ZigZag.CreateDequantizationTable(ref qt); - Assert.Equal(expected, actual); + Block8x8F expected = original; + Block8x8F actual = original; + + ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data); + + actual.MultiplyInplace(ref zigQt); + + this.CompareBlocks(expected, actual, 0); + } + + [Fact] + public void MultiplyInplace_ByScalar() + { + Block8x8F original = CreateRandomFloatBlock(-500, 500); + + Block8x8F actual = original; + actual.MultiplyInplace(42f); + + for (int i = 0; i < 64; i++) + { + Assert.Equal(original[i]*42f, actual[i]); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs new file mode 100644 index 0000000000..c2fa8c8d40 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs @@ -0,0 +1,143 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public class Block8x8Tests : JpegFixture + { + public Block8x8Tests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void Construct_And_Indexer_Get() + { + short[] data = Create8x8ShortData(); + + var block = new Block8x8(data); + + for (int i = 0; i < Block8x8.Size; i++) + { + Assert.Equal(data[i], block[i]); + } + } + + [Fact] + public void Indexer_Set() + { + var block = default(Block8x8); + + block[17] = 17; + block[42] = 42; + + Assert.Equal(0, block[0]); + Assert.Equal(17, block[17]); + Assert.Equal(42, block[42]); + } + + + [Fact] + public unsafe void Indexer_GetScalarAt_SetScalarAt() + { + int sum = 0; + var block = default(Block8x8); + + for (int i = 0; i < Block8x8.Size; i++) + { + Block8x8.SetScalarAt(&block, i, (short)i); + } + + sum = 0; + for (int i = 0; i < Block8x8.Size; i++) + { + sum += Block8x8.GetScalarAt(&block, i); + } + Assert.Equal(sum, 64 * 63 / 2); + } + + + [Fact] + public void AsFloatBlock() + { + short[] data = Create8x8ShortData(); + + var source = new Block8x8(data); + + Block8x8F dest = source.AsFloatBlock(); + + for (int i = 0; i < Block8x8F.Size; i++) + { + Assert.Equal((float)data[i], dest[i]); + } + } + + [Fact] + public void ToArray() + { + short[] data = Create8x8ShortData(); + var block = new Block8x8(data); + + short[] result = block.ToArray(); + + Assert.Equal(data, result); + } + + [Fact] + public void Equality_WhenTrue() + { + short[] data = Create8x8ShortData(); + var block1 = new Block8x8(data); + var block2 = new Block8x8(data); + + block1[0] = 42; + block2[0] = 42; + + Assert.Equal(block1, block2); + Assert.Equal(block1.GetHashCode(), block2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + short[] data = Create8x8ShortData(); + var block1 = new Block8x8(data); + var block2 = new Block8x8(data); + + block1[0] = 42; + block2[0] = 666; + + Assert.NotEqual(block1, block2); + } + + [Fact] + public void IndexerXY() + { + var block = default(Block8x8); + block[8 * 3 + 5] = 42; + + short value = block[5, 3]; + + Assert.Equal(42, value); + } + + [Fact] + public void TotalDifference() + { + short[] data = Create8x8ShortData(); + var block1 = new Block8x8(data); + var block2 = new Block8x8(data); + + block2[10] += 7; + block2[63] += 8; + + long d = Block8x8.TotalDifference(ref block1, ref block2); + + Assert.Equal(15, d); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs new file mode 100644 index 0000000000..ee6f5305fb --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -0,0 +1,182 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public static class DCTTests + { + public class FastFloatingPoint : JpegFixture + { + public FastFloatingPoint(ITestOutputHelper output) + : base(output) + { + } + + + [Fact] + public void iDCT2D8x4_LeftPart() + { + float[] sourceArray = JpegFixture.Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); + + Block8x8F source = new Block8x8F(); + source.LoadFrom(sourceArray); + + Block8x8F dest = new Block8x8F(); + + FastFloatingPointDCT.IDCT8x4_LeftPart(ref source, ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + this.Print8x8Data(expectedDestArray); + this.Output.WriteLine("**************"); + this.Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray, actualDestArray); + } + + [Fact] + public void iDCT2D8x4_RightPart() + { + float[] sourceArray = JpegFixture.Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray.AsSpan().Slice(4), expectedDestArray.AsSpan().Slice(4)); + + Block8x8F source = new Block8x8F(); + source.LoadFrom(sourceArray); + + Block8x8F dest = new Block8x8F(); + + FastFloatingPointDCT.IDCT8x4_RightPart(ref source, ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + this.Print8x8Data(expectedDestArray); + this.Output.WriteLine("**************"); + this.Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray, actualDestArray); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void LLM_TransformIDCT_CompareToNonOptimized(int seed) + { + float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); + + var temp = default(Block8x8F); + var actual = default(Block8x8F); + FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); + + this.CompareBlocks(expected, actual, 1f); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void LLM_TransformIDCT_CompareToAccurate(int seed) + { + float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + + var temp = default(Block8x8F); + var actual = default(Block8x8F); + FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); + + this.CompareBlocks(expected, actual, 1f); + } + + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void FDCT8x4_LeftPart(int seed) + { + Span src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); + Block8x8F srcBlock = new Block8x8F(); + srcBlock.LoadFrom(src); + + Block8x8F destBlock = new Block8x8F(); + + float[] expectedDest = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src, expectedDest); + FastFloatingPointDCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); + + float[] actualDest = new float[64]; + destBlock.CopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void FDCT8x4_RightPart(int seed) + { + Span src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); + Block8x8F srcBlock = new Block8x8F(); + srcBlock.LoadFrom(src); + + Block8x8F destBlock = new Block8x8F(); + + float[] expectedDest = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan().Slice(4)); + FastFloatingPointDCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); + + float[] actualDest = new float[64]; + destBlock.CopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void TransformFDCT(int seed) + { + Span src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); + Block8x8F srcBlock = new Block8x8F(); + srcBlock.LoadFrom(src); + + Block8x8F destBlock = new Block8x8F(); + + float[] expectedDest = new float[64]; + float[] temp1 = new float[64]; + Block8x8F temp2 = new Block8x8F(); + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); + FastFloatingPointDCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); + + float[] actualDest = new float[64]; + destBlock.CopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs new file mode 100644 index 0000000000..cc06d5701b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + + using Xunit; + + public class JFifMarkerTests + { + // Taken from actual test image + private readonly byte[] bytes = { 0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x60, 0x0, 0x60, 0x0 }; + + // Altered components + private readonly byte[] bytes2 = { 0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x48, 0x0, 0x48, 0x0 }; + + // Incorrect density values. Zero is invalid. + private readonly byte[] bytes3 = { 0x4A, 0x46, 0x49, 0x46, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0 }; + + [Fact] + public void MarkerLengthIsCorrect() + { + Assert.Equal(13, JFifMarker.Length); + } + + [Fact] + public void MarkerReturnsCorrectParsedValue() + { + bool isJFif = JFifMarker.TryParse(this.bytes, out var marker); + + Assert.True(isJFif); + Assert.Equal(1, marker.MajorVersion); + Assert.Equal(1, marker.MinorVersion); + Assert.Equal(1, marker.DensityUnits); + Assert.Equal(96, marker.XDensity); + Assert.Equal(96, marker.YDensity); + } + + [Fact] + public void MarkerIgnoresIncorrectValue() + { + bool isJFif = JFifMarker.TryParse(new byte[] { 0, 0, 0, 0 }, out var marker); + + Assert.False(isJFif); + Assert.Equal(default(JFifMarker), marker); + } + + [Fact] + public void MarkerIgnoresCorrectHeaderButInvalidDensities() + { + bool isJFif = JFifMarker.TryParse(this.bytes3, out var marker); + + Assert.False(isJFif); + Assert.Equal(default(JFifMarker), marker); + } + + [Fact] + public void MarkerEqualityIsCorrect() + { + JFifMarker.TryParse(this.bytes, out var marker); + JFifMarker.TryParse(this.bytes, out var marker2); + + Assert.True(marker.Equals(marker2)); + } + + [Fact] + public void MarkerInEqualityIsCorrect() + { + JFifMarker.TryParse(this.bytes, out var marker); + JFifMarker.TryParse(this.bytes2, out var marker2); + + Assert.False(marker.Equals(marker2)); + } + + [Fact] + public void MarkerHashCodeIsReplicable() + { + JFifMarker.TryParse(this.bytes, out var marker); + JFifMarker.TryParse(this.bytes, out var marker2); + + Assert.True(marker.GetHashCode().Equals(marker2.GetHashCode())); + } + + [Fact] + public void MarkerHashCodeIsUnique() + { + JFifMarker.TryParse(this.bytes, out var marker); + JFifMarker.TryParse(this.bytes2, out var marker2); + + Assert.False(marker.GetHashCode().Equals(marker2.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs new file mode 100644 index 0000000000..7e0dc915ce --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -0,0 +1,334 @@ +using System; +using System.Numerics; + +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; + +using Xunit; +using Xunit.Abstractions; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters; + + public class JpegColorConverterTests + { + private const float Precision = 0.1f / 255; + + public static readonly TheoryData CommonConversionData = + new TheoryData + { + { 40, 40, 1 }, + { 42, 40, 2 }, + { 42, 39, 3 } + }; + + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + + public JpegColorConverterTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion(new JpegColorConverter.FromYCbCrBasic(), 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + } + + private static void ValidateYCbCr(JpegColorConverter.ComponentValues values, Span result, int i) + { + float y = values.Component0[i]; + float cb = values.Component1[i]; + float cr = values.Component2[i]; + var ycbcr = new YCbCr(y, cb, cr); + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = ColorSpaceConverter.ToRgb(ycbcr); + + Assert.True(actual.AlmostEquals(expected, Precision), $"{actual} != {expected}"); + Assert.Equal(1, rgba.W); + } + + [Theory] + [InlineData(64, 1)] + [InlineData(16, 2)] + [InlineData(8, 3)] + public void FromYCbCrSimd_ConvertCore(int size, int seed) + { + ValidateConversion(JpegColorConverter.FromYCbCrSimd.ConvertCore, 3, size, size, seed, ValidateYCbCr); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion( + new JpegColorConverter.FromYCbCrSimd(), + 3, + inputBufferLength, + resultBufferLength, + seed, + ValidateYCbCr); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void FromYCbCrSimdAvx2(int inputBufferLength, int resultBufferLength, int seed) + { + if (!SimdUtils.IsAvx2CompatibleArchitecture) + { + this.Output.WriteLine("No AVX2 present, skipping test!"); + return; + } + + //JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s); + + ValidateConversion( + new JpegColorConverter.FromYCbCrSimdAvx2(), + 3, + inputBufferLength, + resultBufferLength, + seed, + ValidateYCbCr); + } + + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion(JpegColorSpace.YCbCr, 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + } + + // Becnhmark, for local execution only + //[Theory] + //[InlineData(false)] + //[InlineData(true)] + public void BenchmarkYCbCr(bool simd) + { + int count = 2053; + int times = 50000; + + JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); + Vector4[] result = new Vector4[count]; + + JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic(); + + // Warm up: + converter.ConvertToRGBA(values, result); + + using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}")) + { + for (int i = 0; i < times; i++) + { + converter.ConvertToRGBA(values, result); + } + } + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromCmyk(int inputBufferLength, int resultBufferLength, int seed) + { + var v = new Vector4(0, 0, 0, 1F); + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + ValidateConversion( + JpegColorSpace.Cmyk, + 4, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float c = values.Component0[i]; + float m = values.Component1[i]; + float y = values.Component2[i]; + float k = values.Component3[i] / 255F; + + v.X = c * k; + v.Y = m * k; + v.Z = y * k; + v.W = 1F; + + v *= scale; + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(v.X, v.Y, v.Z); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion( + JpegColorSpace.GrayScale, + 1, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float y = values.Component0[i]; + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(y / 255F, y / 255F, y / 255F); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion( + JpegColorSpace.RGB, + 3, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float r = values.Component0[i]; + float g = values.Component1[i]; + float b = values.Component2[i]; + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(r / 255F, g / 255F, b / 255F); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromYcck(int inputBufferLength, int resultBufferLength, int seed) + { + var v = new Vector4(0, 0, 0, 1F); + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + ValidateConversion( + JpegColorSpace.Ycck, + 4, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float y = values.Component0[i]; + float cb = values.Component1[i] - 128F; + float cr = values.Component2[i] - 128F; + float k = values.Component3[i] / 255F; + + v.X = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; + v.Y = (255F - (float)Math.Round( + y - (0.344136F * cb) - (0.714136F * cr), + MidpointRounding.AwayFromZero)) * k; + v.Z = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; + v.W = 1F; + + v *= scale; + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(v.X, v.Y, v.Z); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + private static JpegColorConverter.ComponentValues CreateRandomValues( + int componentCount, + int inputBufferLength, + int seed, + float minVal = 0f, + float maxVal = 255f) + { + var rnd = new Random(seed); + Buffer2D[] buffers = new Buffer2D[componentCount]; + for (int i = 0; i < componentCount; i++) + { + float[] values = new float[inputBufferLength]; + + for (int j = 0; j < inputBufferLength; j++) + { + values[j] = (float)rnd.NextDouble() * (maxVal-minVal)+minVal; + } + + // no need to dispose when buffer is not array owner + buffers[i] = new Buffer2D(values, values.Length, 1); + } + return new JpegColorConverter.ComponentValues(buffers, 0); + } + + private static void ValidateConversion( + JpegColorSpace colorSpace, + int componentCount, + int inputBufferLength, + int resultBufferLength, + int seed, + Action, int> validatePixelValue) + { + ValidateConversion( + JpegColorConverter.GetConverter(colorSpace), + componentCount, + inputBufferLength, + resultBufferLength, + seed, + validatePixelValue); + } + + private static void ValidateConversion( + JpegColorConverter converter, + int componentCount, + int inputBufferLength, + int resultBufferLength, + int seed, + Action, int> validatePixelValue) + { + ValidateConversion( + converter.ConvertToRGBA, + componentCount, + inputBufferLength, + resultBufferLength, + seed, + validatePixelValue); + } + + private static void ValidateConversion( + Action> doConvert, + int componentCount, + int inputBufferLength, + int resultBufferLength, + int seed, + Action, int> validatePixelValue) + { + JpegColorConverter.ComponentValues values = CreateRandomValues(componentCount, inputBufferLength, seed); + Vector4[] result = new Vector4[resultBufferLength]; + + doConvert(values, result); + + for (int i = 0; i < resultBufferLength; i++) + { + validatePixelValue(values, result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 9401d098de..cb1987aef4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -1,59 +1,286 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests + +using System; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using System; + using System.Collections.Generic; using System.IO; + using System.Linq; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; + using Xunit.Abstractions; - public class JpegDecoderTests : TestBase + // TODO: Scatter test cases into multiple test classes + public class JpegDecoderTests { public static string[] BaselineTestJpegs = { - TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, - TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Jpeg444, - TestImages.Jpeg.Baseline.Testimgorig + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Testorig420, + + // BUG: The following image has a high difference compared to the expected output: + //TestImages.Jpeg.Baseline.Jpeg420Small, + + TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.Bad.ExifUndefType, + TestImages.Jpeg.Issues.MultiHuffmanBaseline394, }; - public static string[] ProgressiveTestJpegs = TestImages.Jpeg.Progressive.All; + public static string[] ProgressiveTestJpegs = + { + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Issues.BadCoeffsProgressive178, + TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, + TestImages.Jpeg.Issues.BadZigZagProgressive385 + }; + + private static readonly Dictionary CustomToleranceValues = new Dictionary + { + // Baseline: + [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, + [TestImages.Jpeg.Baseline.Bad.ExifUndefType] = 0.011f / 100, + [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, + + // Progressive: + [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, + [TestImages.Jpeg.Issues.BadCoeffsProgressive178] = 0.38f / 100, + [TestImages.Jpeg.Progressive.Bad.BadEOF] = 0.3f / 100, + [TestImages.Jpeg.Progressive.Festzug] = 0.02f / 100, + [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, + [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, + [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, + }; + + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; + + private const float BaselineTolerance_Orig = 0.001f / 100; + private const float BaselineTolerance_PdfJs = 0.005f; + + private const float ProgressiveTolerance_Orig = 0.2f / 100; + private const float ProgressiveTolerance_PdfJs = 1.5f / 100; // PDF.js Progressive output is wrong on spectral level! + + private ImageComparer GetImageComparerForOrigDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + string file = provider.SourceFileOrDescription; + + if (!CustomToleranceValues.TryGetValue(file, out float tolerance)) + { + tolerance = file.ToLower().Contains("baseline") ? BaselineTolerance_Orig : ProgressiveTolerance_Orig; + } + + return ImageComparer.Tolerant(tolerance); + } + + public JpegDecoderTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + private static IImageDecoder OrigJpegDecoder => new OrigJpegDecoder(); + + private static IImageDecoder PdfJsJpegDecoder => new PdfJsJpegDecoder(); + + [Fact] + public void ParseStream_BasicPropertiesAreCorrect1_PdfJs() + { + byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; + using (var ms = new MemoryStream(bytes)) + { + var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + decoder.ParseStream(ms); + + VerifyJpeg.VerifyComponentSizes3(decoder.Frame.Components, 43, 61, 22, 31, 22, 31); + } + } + + public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; + [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32)] - public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) + [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, false)] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, true)] + public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider, bool useOldDecoder) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + IImageDecoder decoder = useOldDecoder ? OrigJpegDecoder : PdfJsJpegDecoder; + using (Image image = provider.GetImage(decoder)) { - provider.Utility.SaveTestOutputFile(image, "bmp"); + image.DebugSave(provider); + + provider.Utility.TestName = DecodeBaselineJpegOutputName; + image.CompareToReferenceOutput(provider, ImageComparer.Tolerant(BaselineTolerance_PdfJs), appendPixelTypeToFileName: false); } } [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32)] - public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] + public void DecodeBaselineJpeg_Orig(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage(OrigJpegDecoder)) + { + image.DebugSave(provider); + provider.Utility.TestName = DecodeBaselineJpegOutputName; + image.CompareToReferenceOutput( + provider, + this.GetImageComparerForOrigDecoder(provider), + appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] + public void DecodeBaselineJpeg_PdfJs(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(PdfJsJpegDecoder)) + { + image.DebugSave(provider); + + provider.Utility.TestName = DecodeBaselineJpegOutputName; + image.CompareToReferenceOutput( + provider, + ImageComparer.Tolerant(BaselineTolerance_PdfJs), + appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] + public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow_Orig(TestImageProvider provider) + where TPixel : struct, IPixel + { + // TODO: We need a public ImageDecoderException class in ImageSharp! + Assert.ThrowsAny(() => provider.GetImage(OrigJpegDecoder)); + } + + public const string DecodeProgressiveJpegOutputName = "DecodeProgressiveJpeg"; + + [Theory] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] + public void DecodeProgressiveJpeg_Orig(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(OrigJpegDecoder)) { - provider.Utility.SaveTestOutputFile(image, "bmp"); + image.DebugSave(provider); + + provider.Utility.TestName = DecodeProgressiveJpegOutputName; + image.CompareToReferenceOutput( + provider, + this.GetImageComparerForOrigDecoder(provider), + appendPixelTypeToFileName: false); } } + [Theory] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] + public void DecodeProgressiveJpeg_PdfJs(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(PdfJsJpegDecoder)) + { + image.DebugSave(provider); + + provider.Utility.TestName = DecodeProgressiveJpegOutputName; + image.CompareToReferenceOutput( + provider, + ImageComparer.Tolerant(ProgressiveTolerance_PdfJs), + appendPixelTypeToFileName: false); + } + } + + private string GetDifferenceInPercentageString(Image image, TestImageProvider provider) + where TPixel : struct, IPixel + { + var reportingComparer = ImageComparer.Tolerant(0, 0); + + ImageSimilarityReport report = image.GetReferenceOutputSimilarityReports( + provider, + reportingComparer, + appendPixelTypeToFileName: false + ).SingleOrDefault(); + + if (report != null && report.TotalNormalizedDifference.HasValue) + { + return report.DifferencePercentageString; + } + + return "0%"; + } + + private void CompareJpegDecodersImpl(TestImageProvider provider, string testName) + where TPixel : struct, IPixel + { + if (TestEnvironment.RunsOnCI) // Debug only test + { + return; + } + + this.Output.WriteLine(provider.SourceFileOrDescription); + provider.Utility.TestName = testName; + + using (Image image = provider.GetImage(OrigJpegDecoder)) + { + string d = this.GetDifferenceInPercentageString(image, provider); + + this.Output.WriteLine($"Difference using ORIGINAL decoder: {d}"); + } + + using (Image image = provider.GetImage(PdfJsJpegDecoder)) + { + string d = this.GetDifferenceInPercentageString(image, provider); + this.Output.WriteLine($"Difference using PDFJS decoder: {d}"); + } + } + + [Theory] + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] + public void CompareJpegDecoders_Baseline(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.CompareJpegDecodersImpl(provider, DecodeBaselineJpegOutputName); + } + + [Theory] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] + public void CompareJpegDecoders_Progressive(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.CompareJpegDecodersImpl(provider, DecodeProgressiveJpegOutputName); + } + [Theory] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio420, 75)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio420, 100)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 75)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 100)] [WithSolidFilledImages(8, 8, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 100)] - public void DecodeGenerated_SaveBmp( + public void DecodeGenerated_Orig( TestImageProvider provider, JpegSubsample subsample, int quality) @@ -62,43 +289,17 @@ namespace ImageSharp.Tests byte[] data; using (Image image = provider.GetImage()) { - JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; + var encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; data = new byte[65536]; - using (MemoryStream ms = new MemoryStream(data)) + using (var ms = new MemoryStream(data)) { image.Save(ms, encoder); } } - // TODO: Automatic image comparers could help here a lot :P - Image mirror = provider.Factory.CreateImage(data); - provider.Utility.TestName += $"_{subsample}_Q{quality}"; - provider.Utility.SaveTestOutputFile(mirror, "bmp"); - } - - [Theory] - [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.Rgba32)] - public void DecodeGenerated_MetadataOnly( - TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - using (MemoryStream ms = new MemoryStream()) - { - image.Save(ms, new JpegEncoder()); - ms.Seek(0, SeekOrigin.Begin); - - using (JpegDecoderCore decoder = new JpegDecoderCore(null, new JpegDecoder())) - { - Image mirror = decoder.Decode(ms); - - Assert.Equal(decoder.ImageWidth, image.Width); - Assert.Equal(decoder.ImageHeight, image.Height); - } - } - } + var mirror = Image.Load(data, OrigJpegDecoder); + mirror.DebugSave(provider, $"_{subsample}_Q{quality}"); } [Fact] @@ -114,7 +315,7 @@ namespace ImageSharp.Tests [Fact] public void Decoder_Reads_Correct_Resolution_From_Exif() { - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420Exif).CreateImage()) { Assert.Equal(72, image.MetaData.HorizontalResolution); Assert.Equal(72, image.MetaData.VerticalResolution); @@ -124,12 +325,12 @@ namespace ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsFalse_ExifProfileIsRead() { - JpegDecoder decoder = new JpegDecoder() + var decoder = new JpegDecoder() { IgnoreMetadata = false }; - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); using (Image image = testFile.CreateImage(decoder)) { @@ -140,17 +341,65 @@ namespace ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored() { - JpegDecoder options = new JpegDecoder() + var options = new JpegDecoder() { IgnoreMetadata = true }; - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); using (Image image = testFile.CreateImage(options)) { Assert.Null(image.MetaData.ExifProfile); } } + + // DEBUG ONLY! + // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" + // into "\tests\Images\ActualOutput\JpegDecoderTests\" + //[Theory] + //[WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32, "PdfJsOriginal_progress.png")] + public void ValidateProgressivePdfJsOutput(TestImageProvider provider, + string pdfJsOriginalResultImage) + where TPixel : struct, IPixel + { + // tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm + string pdfJsOriginalResultPath = Path.Combine( + provider.Utility.GetTestOutputDir(), + pdfJsOriginalResultImage); + + byte[] sourceBytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; + + provider.Utility.TestName = nameof(DecodeProgressiveJpegOutputName); + + var comparer = ImageComparer.Tolerant(0, 0); + + using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) + using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) + using (var pdfJsPortResult = Image.Load(sourceBytes, PdfJsJpegDecoder)) + { + ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); + ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); + + this.Output.WriteLine($"Difference for PDF.js ORIGINAL: {originalReport.DifferencePercentageString}"); + this.Output.WriteLine($"Difference for PORT: {portReport.DifferencePercentageString}"); + } + } + + [Theory] + [InlineData(TestImages.Jpeg.Progressive.Progress, 24)] + [InlineData(TestImages.Jpeg.Progressive.Fb, 24)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk, 32)] + [InlineData(TestImages.Jpeg.Baseline.Ycck, 32)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg400, 8)] + [InlineData(TestImages.Jpeg.Baseline.Snake, 24)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 86faeee232..c8d416beaf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -1,24 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ImageSharp.Formats; -using Xunit; -using Xunit.Abstractions; + + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using ImageSharp.Formats.Jpg; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; + using System.Collections.Generic; + using System.IO; + + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; + using Xunit; + using Xunit.Abstractions; + public class JpegEncoderTests : MeasureFixture { public static IEnumerable AllBmpFiles => TestImages.Bmp.All; @@ -36,8 +35,9 @@ namespace ImageSharp.Tests public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) where TPixel : struct, IPixel { - using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) + using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) { + image.MetaData.ExifProfile = null; // Reduce the size of the file JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; @@ -62,8 +62,8 @@ namespace ImageSharp.Tests { image.Save(outputStream, new JpegEncoder() { - Subsample = subSample, - Quality = quality + Subsample = subSample, + Quality = quality }); } } @@ -83,7 +83,7 @@ namespace ImageSharp.Tests { using (MemoryStream memStream = new MemoryStream()) { - input.Save(memStream, options); + input.Save(memStream, options); memStream.Position = 0; using (Image output = Image.Load(memStream)) @@ -118,5 +118,51 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void Encode_Quality_0_And_1_Are_Identical() + { + var options = new JpegEncoder + { + Quality = 0 + }; + + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); + + using (Image input = testFile.CreateImage()) + using (var memStream0 = new MemoryStream()) + using (var memStream1 = new MemoryStream()) + { + input.SaveAsJpeg(memStream0, options); + + options.Quality = 1; + input.SaveAsJpeg(memStream1, options); + + Assert.Equal(memStream0.ToArray(), memStream1.ToArray()); + } + } + + [Fact] + public void Encode_Quality_0_And_100_Are_Not_Identical() + { + var options = new JpegEncoder + { + Quality = 0 + }; + + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); + + using (Image input = testFile.CreateImage()) + using (var memStream0 = new MemoryStream()) + using (var memStream1 = new MemoryStream()) + { + input.SaveAsJpeg(memStream0, options); + + options.Quality = 100; + input.SaveAsJpeg(memStream1, options); + + Assert.NotEqual(memStream0.ToArray(), memStream1.ToArray()); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs new file mode 100644 index 0000000000..df6d1ef1bb --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -0,0 +1,107 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Xunit; + using Xunit.Abstractions; + + public class JpegImagePostProcessorTests + { + public static string[] BaselineTestJpegs = + { + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, + TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.Bad.ExifUndefType, + }; + + public static string[] ProgressiveTestJpegs = + { + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + }; + + public JpegImagePostProcessorTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + private static void SaveBuffer(JpegComponentPostProcessor cp, TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = cp.ColorBuffer.ToGrayscaleImage(1f / 255f)) + { + image.DebugSave(provider, $"-C{cp.Component.Index}-"); + } + + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)] + public void DoProcessorStep(TestImageProvider provider) + where TPixel : struct, IPixel + { + string imageFile = provider.SourceFileOrDescription; + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) + using (var pp = new JpegImagePostProcessor(decoder)) + using (var imageFrame = new ImageFrame(decoder.ImageWidth, decoder.ImageHeight)) + { + pp.DoPostProcessorStep(imageFrame); + + JpegComponentPostProcessor[] cp = pp.ComponentProcessors; + + SaveBuffer(cp[0], provider); + SaveBuffer(cp[1], provider); + SaveBuffer(cp[2], provider); + } + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)] + public void PostProcess(TestImageProvider provider) + where TPixel : struct, IPixel + { + string imageFile = provider.SourceFileOrDescription; + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) + using (var pp = new JpegImagePostProcessor(decoder)) + using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) + { + pp.PostProcess(image.Frames.RootFrame); + + image.DebugSave(provider); + + ImagingTestCaseUtility testUtil = provider.Utility; + testUtil.TestGroupName = nameof(JpegDecoderTests); + testUtil.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName; + + using (Image referenceImage = + provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) + { + ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); + + this.Output.WriteLine($"*** {imageFile} ***"); + this.Output.WriteLine($"Difference: "+ report.DifferencePercentageString); + + // ReSharper disable once PossibleInvalidOperationException + Assert.True(report.TotalNormalizedDifference.Value < 0.005f); + } + } + + + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index daf8da6a38..6652db577c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { using System; using System.IO; using System.Linq; using System.Numerics; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using Xunit; using Xunit.Abstractions; @@ -29,14 +30,32 @@ namespace ImageSharp.Tests TestImages.Jpeg.Baseline.Ycck, TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Jpeg400, - TestImages.Jpeg.Baseline.Jpeg420, + TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Jpeg444, }; + //[Theory] // Benchmark, enable manually + //[MemberData(nameof(DecodeJpegData))] + public void DecodeJpeg_Original(string fileName) + { + this.DecodeJpegBenchmarkImpl(fileName, new OrigJpegDecoder()); + } + // [Theory] // Benchmark, enable manually // [MemberData(nameof(DecodeJpegData))] - public void DecodeJpeg(string fileName) + public void DecodeJpeg_PdfJs(string fileName) { + this.DecodeJpegBenchmarkImpl(fileName, new PdfJsJpegDecoder()); + } + + private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder) + { + // do not run this on CI even by accident + if (TestEnvironment.RunsOnCI) + { + return; + } + const int ExecutionCount = 30; if (!Vector.IsHardwareAccelerated) @@ -44,18 +63,17 @@ namespace ImageSharp.Tests throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); } - string path = TestFile.GetPath(fileName); + string path = TestFile.GetInputFileFullPath(fileName); byte[] bytes = File.ReadAllBytes(path); this.Measure( ExecutionCount, () => { - Image img = Image.Load(bytes); + Image img = Image.Load(bytes, decoder); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); - } // Benchmark, enable manually! @@ -66,6 +84,12 @@ namespace ImageSharp.Tests // [InlineData(30, 100, JpegSubsample.Ratio444)] public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) { + // do not run this on CI even by accident + if (TestEnvironment.RunsOnCI) + { + return; + } + string[] testFiles = TestImages.Bmp.All .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }) .ToArray(); @@ -94,4 +118,4 @@ namespace ImageSharp.Tests } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs deleted file mode 100644 index 252b01138b..0000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs +++ /dev/null @@ -1,107 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -using System.Text; - -using Xunit.Abstractions; -// ReSharper disable InconsistentNaming - -namespace ImageSharp.Tests -{ - using System; - using System.Diagnostics; - - using ImageSharp.Formats.Jpg; - - public class JpegUtilityTestFixture : MeasureFixture - { - public JpegUtilityTestFixture(ITestOutputHelper output) : base(output) - { - } - - // ReSharper disable once InconsistentNaming - public static float[] Create8x8FloatData() - { - float[] result = new float[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i * 8 + j] = i * 10 + j; - } - } - return result; - } - - // ReSharper disable once InconsistentNaming - public static int[] Create8x8IntData() - { - int[] result = new int[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i * 8 + j] = i * 10 + j; - } - } - return result; - } - - // ReSharper disable once InconsistentNaming - public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) - { - Random rnd = new Random(seed); - int[] result = new int[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i * 8 + j] = rnd.Next(minValue, maxValue); - } - } - return result; - } - - internal static MutableSpan Create8x8RandomFloatData(int minValue, int maxValue, int seed = 42) - => new MutableSpan(Create8x8RandomIntData(minValue, maxValue, seed)).ConvertToFloat32MutableSpan(); - - internal void Print8x8Data(MutableSpan data) => this.Print8x8Data(data.Data); - - internal void Print8x8Data(T[] data) - { - StringBuilder bld = new StringBuilder(); - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - bld.Append($"{data[i * 8 + j],3} "); - } - bld.AppendLine(); - } - - this.Output.WriteLine(bld.ToString()); - } - - internal void PrintLinearData(T[] data) => this.PrintLinearData(new MutableSpan(data), data.Length); - - internal void PrintLinearData(MutableSpan data, int count = -1) - { - if (count < 0) count = data.TotalCount; - - StringBuilder bld = new StringBuilder(); - for (int i = 0; i < count; i++) - { - bld.Append($"{data[i],3} "); - } - this.Output.WriteLine(bld.ToString()); - } - - protected void Print(string msg) - { - Debug.WriteLine(msg); - this.Output.WriteLine(msg); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index f681e1d8f9..887e9d7e95 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -1,34 +1,33 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using System; using System.Numerics; - using ImageSharp.Formats.Jpg; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; + using SixLabors.ImageSharp.PixelFormats; using Xunit; - public class JpegUtilsTests : TestBase + public class JpegUtilsTests { - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + var image = new Image(10, 10); using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { - Vector4 v = new Vector4(i / 10f, j / 10f, 0, 1); + var v = new Vector4(i / 10f, j / 10f, 0, 1); - TPixel color = default(TPixel); + var color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -45,7 +44,7 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { using (Image src = provider.GetImage()) - using (Image dest = provider.Factory.CreateImage(8, 8)) + using (Image dest = new Image(8,8)) using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) using (PixelAccessor s = src.Lock()) using (PixelAccessor d = dest.Lock()) @@ -68,7 +67,7 @@ namespace ImageSharp.Tests { using (Image src = provider.GetImage()) using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) - using (Image dest = provider.Factory.CreateImage(8, 8)) + using (Image dest = new Image(8, 8)) using (PixelAccessor s = src.Lock()) using (PixelAccessor d = dest.Lock()) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/LibJpegToolsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/LibJpegToolsTests.cs new file mode 100644 index 0000000000..773d7112b6 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/LibJpegToolsTests.cs @@ -0,0 +1,52 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System.IO; + + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + + public class LibJpegToolsTests + { + [Fact] + public void RunDumpJpegCoeffsTool() + { + if (!TestEnvironment.IsWindows) return; + + string inputFile = TestFile.GetInputFileFullPath(TestImages.Jpeg.Progressive.Progress); + string outputDir = TestEnvironment.CreateOutputDirectory(nameof(SpectralJpegTests)); + string outputFile = Path.Combine(outputDir, "progress.dctdump"); + + LibJpegTools.RunDumpJpegCoeffsTool(inputFile, outputFile); + + Assert.True(File.Exists(outputFile)); + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32)] + public void ExtractSpectralData(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.IsWindows) + { + return; + } + + string testImage = provider.SourceFileOrDescription; + LibJpegTools.SpectralData data = LibJpegTools.ExtractSpectralData(testImage); + + Assert.True(data.ComponentCount == 3); + Assert.True(data.Components.Length == 3); + + VerifyJpeg.SaveSpectralImage(provider, data); + + // I knew this one well: + if (testImage == TestImages.Jpeg.Progressive.Progress) + { + VerifyJpeg.VerifyComponentSizes3(data.Components, 43, 61, 22, 31, 22, 31); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs new file mode 100644 index 0000000000..e56e912074 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -0,0 +1,134 @@ +using System; + +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; +using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; +using SixLabors.Primitives; +using Xunit; +using Xunit.Abstractions; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System.Text; + + public class ParseStreamTests + { + private ITestOutputHelper Output { get; } + + public ParseStreamTests(ITestOutputHelper output) + { + this.Output = output; + } + + [Theory] + [InlineData(TestImages.Jpeg.Baseline.Testorig420, JpegColorSpace.YCbCr)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.GrayScale)] + [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] + public void ColorSpace_IsDeducedCorrectly(string imageFile, object expectedColorSpaceValue) + { + var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue; + + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, true)) + { + Assert.Equal(expecteColorSpace, decoder.ColorSpace); + } + } + + [Fact] + public void ComponentScalingIsCorrect_1ChannelJpeg() + { + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(TestImages.Jpeg.Baseline.Jpeg400, true)) + { + Assert.Equal(1, decoder.ComponentCount); + Assert.Equal(1, decoder.Components.Length); + + Size expectedSizeInBlocks = decoder.ImageSizeInPixels.DivideRoundUp(8); + + Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); + + var uniform1 = new Size(1, 1); + OrigComponent c0 = decoder.Components[0]; + VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); + } + } + + [Theory] + [InlineData(TestImages.Jpeg.Baseline.Jpeg444)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small)] + [InlineData(TestImages.Jpeg.Baseline.Testorig420)] + [InlineData(TestImages.Jpeg.Baseline.Ycck)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk)] + public void PrintComponentData(string imageFile) + { + StringBuilder bld = new StringBuilder(); + + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, true)) + { + bld.AppendLine(imageFile); + bld.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); + OrigComponent c0 = decoder.Components[0]; + OrigComponent c1 = decoder.Components[1]; + + bld.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); + bld.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); + } + this.Output.WriteLine(bld.ToString()); + } + + public static readonly TheoryData ComponentVerificationData = new TheoryData() + { + { TestImages.Jpeg.Baseline.Jpeg444, 3, new Size(1, 1), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Jpeg420Exif, 3, new Size(2, 2), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Jpeg420Small, 3, new Size(2, 2), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Testorig420, 3, new Size(2, 2), new Size(1, 1) }, + // TODO: Find Ycck or Cmyk images with different subsampling + { TestImages.Jpeg.Baseline.Ycck, 4, new Size(1, 1), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Cmyk, 4, new Size(1, 1), new Size(1, 1) }, + }; + + [Theory] + [MemberData(nameof(ComponentVerificationData))] + public void ComponentScalingIsCorrect_MultiChannelJpeg( + string imageFile, + int componentCount, + object expectedLumaFactors, + object expectedChromaFactors) + { + Size fLuma = (Size)expectedLumaFactors; + Size fChroma = (Size)expectedChromaFactors; + + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, true)) + { + Assert.Equal(componentCount, decoder.ComponentCount); + Assert.Equal(componentCount, decoder.Components.Length); + + OrigComponent c0 = decoder.Components[0]; + OrigComponent c1 = decoder.Components[1]; + OrigComponent c2 = decoder.Components[2]; + + var uniform1 = new Size(1, 1); + + Size expectedLumaSizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(fLuma) ; + + Size divisor = fLuma.DivideBy(fChroma); + + Size expectedChromaSizeInBlocks = expectedLumaSizeInBlocks.DivideRoundUp(divisor); + + VerifyJpeg.VerifyComponent(c0, expectedLumaSizeInBlocks, fLuma, uniform1); + VerifyJpeg.VerifyComponent(c1, expectedChromaSizeInBlocks, fChroma, divisor); + VerifyJpeg.VerifyComponent(c2, expectedChromaSizeInBlocks, fChroma, divisor); + + if (componentCount == 4) + { + OrigComponent c3 = decoder.Components[2]; + VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs new file mode 100644 index 0000000000..1d368d1f5b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System.Text; + + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + + using Xunit; + + public class ProfileResolverTests + { + private static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0"); + private static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0"); + private static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0"); + private static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe"); + + [Fact] + public void ProfileResolverHasCorrectJFifMarker() + { + Assert.Equal(JFifMarker, ProfileResolver.JFifMarker); + } + + [Fact] + public void ProfileResolverHasCorrectExifMarker() + { + Assert.Equal(ExifMarker, ProfileResolver.ExifMarker); + } + + [Fact] + public void ProfileResolverHasCorrectIccMarker() + { + Assert.Equal(IccMarker, ProfileResolver.IccMarker); + } + + [Fact] + public void ProfileResolverHasCorrectAdobeMarker() + { + Assert.Equal(AdobeMarker, ProfileResolver.AdobeMarker); + } + + [Fact] + public void ProfileResolverCanResolveJFifMarker() + { + Assert.True(ProfileResolver.IsProfile(JFifMarker, ProfileResolver.JFifMarker)); + } + + [Fact] + public void ProfileResolverCanResolveExifMarker() + { + Assert.True(ProfileResolver.IsProfile(ExifMarker, ProfileResolver.ExifMarker)); + } + + [Fact] + public void ProfileResolverCanResolveIccMarker() + { + Assert.True(ProfileResolver.IsProfile(IccMarker, ProfileResolver.IccMarker)); + } + + [Fact] + public void ProfileResolverCanResolveAdobeMarker() + { + Assert.True(ProfileResolver.IsProfile(AdobeMarker, ProfileResolver.AdobeMarker)); + } + + [Fact] + public void ProfileResolverCorrectlyReportsNonMarker() + { + Assert.False(ProfileResolver.IsProfile(IccMarker, ProfileResolver.AdobeMarker)); + } + + [Fact] + public void ProfileResolverCanHandleIncorrectLength() + { + Assert.False(ProfileResolver.IsProfile(AdobeMarker, ProfileResolver.IccMarker)); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs deleted file mode 100644 index e76a11cec9..0000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs +++ /dev/null @@ -1,919 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -// ReSharper disable InconsistentNaming - -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.Formats; - using ImageSharp.Formats.Jpg; - - /// - /// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests - /// Floating point DCT code Ported from https://github.com/norishigefukushima/dct_simd - /// - internal static class ReferenceImplementations - { - /// - /// Transpose 8x8 block stored linearly in a (inplace) - /// - /// - internal static void Transpose8x8(MutableSpan data) - { - for (int i = 1; i < 8; i++) - { - int i8 = i * 8; - for (int j = 0; j < i; j++) - { - float tmp = data[i8 + j]; - data[i8 + j] = data[j * 8 + i]; - data[j * 8 + i] = tmp; - } - } - } - - /// - /// Transpose 8x8 block stored linearly in a - /// - internal static void Transpose8x8(MutableSpan src, MutableSpan dest) - { - for (int i = 0; i < 8; i++) - { - int i8 = i * 8; - for (int j = 0; j < 8; j++) - { - dest[j * 8 + i] = src[i8 + j]; - } - } - } - - /// - /// The "original" libjpeg/golang based DCT implementation is used as reference implementation for tests. - /// - public static class IntegerReferenceDCT - { - private const int fix_0_298631336 = 2446; - private const int fix_0_390180644 = 3196; - private const int fix_0_541196100 = 4433; - private const int fix_0_765366865 = 6270; - private const int fix_0_899976223 = 7373; - private const int fix_1_175875602 = 9633; - private const int fix_1_501321110 = 12299; - private const int fix_1_847759065 = 15137; - private const int fix_1_961570560 = 16069; - private const int fix_2_053119869 = 16819; - private const int fix_2_562915447 = 20995; - private const int fix_3_072711026 = 25172; - - /// - /// The number of bits - /// - private const int Bits = 13; - - /// - /// The number of bits to shift by on the first pass. - /// - private const int Pass1Bits = 2; - - /// - /// The value to shift by - /// - private const int CenterJSample = 128; - - /// - /// Performs a forward DCT on an 8x8 block of coefficients, including a level shift. - /// Leave results scaled up by an overall factor of 8. - /// - /// The block of coefficients. - public static void TransformFDCTInplace(MutableSpan block) - { - // Pass 1: process rows. - for (int y = 0; y < 8; y++) - { - int y8 = y * 8; - - int x0 = block[y8]; - int x1 = block[y8 + 1]; - int x2 = block[y8 + 2]; - int x3 = block[y8 + 3]; - int x4 = block[y8 + 4]; - int x5 = block[y8 + 5]; - int x6 = block[y8 + 6]; - int x7 = block[y8 + 7]; - - int tmp0 = x0 + x7; - int tmp1 = x1 + x6; - int tmp2 = x2 + x5; - int tmp3 = x3 + x4; - - int tmp10 = tmp0 + tmp3; - int tmp12 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp13 = tmp1 - tmp2; - - tmp0 = x0 - x7; - tmp1 = x1 - x6; - tmp2 = x2 - x5; - tmp3 = x3 - x4; - - block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits; - block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits; - int z1 = (tmp12 + tmp13) * fix_0_541196100; - z1 += 1 << (Bits - Pass1Bits - 1); - block[y8 + 2] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits - Pass1Bits); - block[y8 + 6] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits - Pass1Bits); - - tmp10 = tmp0 + tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp0 + tmp2; - tmp13 = tmp1 + tmp3; - z1 = (tmp12 + tmp13) * fix_1_175875602; - z1 += 1 << (Bits - Pass1Bits - 1); - tmp0 = tmp0 * fix_1_501321110; - tmp1 = tmp1 * fix_3_072711026; - tmp2 = tmp2 * fix_2_053119869; - tmp3 = tmp3 * fix_0_298631336; - tmp10 = tmp10 * -fix_0_899976223; - tmp11 = tmp11 * -fix_2_562915447; - tmp12 = tmp12 * -fix_0_390180644; - tmp13 = tmp13 * -fix_1_961570560; - - tmp12 += z1; - tmp13 += z1; - block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits); - block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits); - block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits); - block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits); - } - - // Pass 2: process columns. - // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8. - for (int x = 0; x < 8; x++) - { - int tmp0 = block[x] + block[56 + x]; - int tmp1 = block[8 + x] + block[48 + x]; - int tmp2 = block[16 + x] + block[40 + x]; - int tmp3 = block[24 + x] + block[32 + x]; - - int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1)); - int tmp12 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp13 = tmp1 - tmp2; - - tmp0 = block[x] - block[56 + x]; - tmp1 = block[8 + x] - block[48 + x]; - tmp2 = block[16 + x] - block[40 + x]; - tmp3 = block[24 + x] - block[32 + x]; - - block[x] = (tmp10 + tmp11) >> Pass1Bits; - block[32 + x] = (tmp10 - tmp11) >> Pass1Bits; - - int z1 = (tmp12 + tmp13) * fix_0_541196100; - z1 += 1 << (Bits + Pass1Bits - 1); - block[16 + x] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits + Pass1Bits); - block[48 + x] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits + Pass1Bits); - - tmp10 = tmp0 + tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp0 + tmp2; - tmp13 = tmp1 + tmp3; - z1 = (tmp12 + tmp13) * fix_1_175875602; - z1 += 1 << (Bits + Pass1Bits - 1); - tmp0 = tmp0 * fix_1_501321110; - tmp1 = tmp1 * fix_3_072711026; - tmp2 = tmp2 * fix_2_053119869; - tmp3 = tmp3 * fix_0_298631336; - tmp10 = tmp10 * -fix_0_899976223; - tmp11 = tmp11 * -fix_2_562915447; - tmp12 = tmp12 * -fix_0_390180644; - tmp13 = tmp13 * -fix_1_961570560; - - tmp12 += z1; - tmp13 += z1; - block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits); - block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits); - block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits); - block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits); - } - - } - private const int w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16) - private const int w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16) - private const int w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16) - private const int w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16) - private const int w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16) - private const int w7 = 565; // 2048*sqrt(2)*cos(7*pi/16) - - private const int w1pw7 = w1 + w7; - private const int w1mw7 = w1 - w7; - private const int w2pw6 = w2 + w6; - private const int w2mw6 = w2 - w6; - private const int w3pw5 = w3 + w5; - private const int w3mw5 = w3 - w5; - - private const int r2 = 181; // 256/sqrt(2) - - /// - /// Performs a 2-D Inverse Discrete Cosine Transformation. - /// - /// The input coefficients should already have been multiplied by the - /// appropriate quantization table. We use fixed-point computation, with the - /// number of bits for the fractional component varying over the intermediate - /// stages. - /// - /// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the - /// discrete W transform and for the discrete Fourier transform", IEEE Trans. on - /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. - /// - /// The source block of coefficients - public static void TransformIDCTInplace(MutableSpan src) - { - // Horizontal 1-D IDCT. - for (int y = 0; y < 8; y++) - { - int y8 = y * 8; - - // If all the AC components are zero, then the IDCT is trivial. - if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 && - src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0) - { - int dc = src[y8 + 0] << 3; - src[y8 + 0] = dc; - src[y8 + 1] = dc; - src[y8 + 2] = dc; - src[y8 + 3] = dc; - src[y8 + 4] = dc; - src[y8 + 5] = dc; - src[y8 + 6] = dc; - src[y8 + 7] = dc; - continue; - } - - // Prescale. - int x0 = (src[y8 + 0] << 11) + 128; - int x1 = src[y8 + 4] << 11; - int x2 = src[y8 + 6]; - int x3 = src[y8 + 2]; - int x4 = src[y8 + 1]; - int x5 = src[y8 + 7]; - int x6 = src[y8 + 5]; - int x7 = src[y8 + 3]; - - // Stage 1. - int x8 = w7 * (x4 + x5); - x4 = x8 + (w1mw7 * x4); - x5 = x8 - (w1pw7 * x5); - x8 = w3 * (x6 + x7); - x6 = x8 - (w3mw5 * x6); - x7 = x8 - (w3pw5 * x7); - - // Stage 2. - x8 = x0 + x1; - x0 -= x1; - x1 = w6 * (x3 + x2); - x2 = x1 - (w2pw6 * x2); - x3 = x1 + (w2mw6 * x3); - x1 = x4 + x6; - x4 -= x6; - x6 = x5 + x7; - x5 -= x7; - - // Stage 3. - x7 = x8 + x3; - x8 -= x3; - x3 = x0 + x2; - x0 -= x2; - x2 = ((r2 * (x4 + x5)) + 128) >> 8; - x4 = ((r2 * (x4 - x5)) + 128) >> 8; - - // Stage 4. - src[y8 + 0] = (x7 + x1) >> 8; - src[y8 + 1] = (x3 + x2) >> 8; - src[y8 + 2] = (x0 + x4) >> 8; - src[y8 + 3] = (x8 + x6) >> 8; - src[y8 + 4] = (x8 - x6) >> 8; - src[y8 + 5] = (x0 - x4) >> 8; - src[y8 + 6] = (x3 - x2) >> 8; - src[y8 + 7] = (x7 - x1) >> 8; - } - - // Vertical 1-D IDCT. - for (int x = 0; x < 8; x++) - { - // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial. - // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so - // we do not bother to check for the all-zero case. - - // Prescale. - int y0 = (src[x] << 8) + 8192; - int y1 = src[32 + x] << 8; - int y2 = src[48 + x]; - int y3 = src[16 + x]; - int y4 = src[8 + x]; - int y5 = src[56 + x]; - int y6 = src[40 + x]; - int y7 = src[24 + x]; - - // Stage 1. - int y8 = (w7 * (y4 + y5)) + 4; - y4 = (y8 + (w1mw7 * y4)) >> 3; - y5 = (y8 - (w1pw7 * y5)) >> 3; - y8 = (w3 * (y6 + y7)) + 4; - y6 = (y8 - (w3mw5 * y6)) >> 3; - y7 = (y8 - (w3pw5 * y7)) >> 3; - - // Stage 2. - y8 = y0 + y1; - y0 -= y1; - y1 = (w6 * (y3 + y2)) + 4; - y2 = (y1 - (w2pw6 * y2)) >> 3; - y3 = (y1 + (w2mw6 * y3)) >> 3; - y1 = y4 + y6; - y4 -= y6; - y6 = y5 + y7; - y5 -= y7; - - // Stage 3. - y7 = y8 + y3; - y8 -= y3; - y3 = y0 + y2; - y0 -= y2; - y2 = ((r2 * (y4 + y5)) + 128) >> 8; - y4 = ((r2 * (y4 - y5)) + 128) >> 8; - - // Stage 4. - src[x] = (y7 + y1) >> 14; - src[8 + x] = (y3 + y2) >> 14; - src[16 + x] = (y0 + y4) >> 14; - src[24 + x] = (y8 + y6) >> 14; - src[32 + x] = (y8 - y6) >> 14; - src[40 + x] = (y0 - y4) >> 14; - src[48 + x] = (y3 - y2) >> 14; - src[56 + x] = (y7 - y1) >> 14; - } - } - } - - /// - /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200 - /// - /// - /// - private static void iDCT1Dllm_32f(MutableSpan y, MutableSpan x) - { - float a0, a1, a2, a3, b0, b1, b2, b3; - float z0, z1, z2, z3, z4; - - //float r0 = 1.414214f; - float r1 = 1.387040f; - float r2 = 1.306563f; - float r3 = 1.175876f; - //float r4 = 1.000000f; - float r5 = 0.785695f; - float r6 = 0.541196f; - float r7 = 0.275899f; - - z0 = y[1] + y[7]; - z1 = y[3] + y[5]; - z2 = y[3] + y[7]; - z3 = y[1] + y[5]; - z4 = (z0 + z1) * r3; - - z0 = z0 * (-r3 + r7); - z1 = z1 * (-r3 - r1); - z2 = z2 * (-r3 - r5) + z4; - z3 = z3 * (-r3 + r5) + z4; - - b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2; - b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3; - b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2; - b0 = y[1] * (r1 + r3 - r5 - r7) + z0 + z3; - - z4 = (y[2] + y[6]) * r6; - z0 = y[0] + y[4]; - z1 = y[0] - y[4]; - z2 = z4 - y[6] * (r2 + r6); - z3 = z4 + y[2] * (r2 - r6); - a0 = z0 + z3; - a3 = z0 - z3; - a1 = z1 + z2; - a2 = z1 - z2; - - x[0] = a0 + b0; - x[7] = a0 - b0; - x[1] = a1 + b1; - x[6] = a1 - b1; - x[2] = a2 + b2; - x[5] = a2 - b2; - x[3] = a3 + b3; - x[4] = a3 - b3; - } - - /// - /// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 - /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp" - /// - /// - /// - /// - internal static void iDCT2D_llm(MutableSpan s, MutableSpan d, MutableSpan temp) - { - int j; - - for (j = 0; j < 8; j++) - { - iDCT1Dllm_32f(s.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - for (j = 0; j < 8; j++) - { - iDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - for (j = 0; j < 64; j++) - { - d[j] *= 0.125f; - } - } - - /// - /// Original: - /// - /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15 - /// - /// - /// Source - /// Destination - public static void fDCT2D8x4_32f(MutableSpan s, MutableSpan d) - { - Vector4 c0 = _mm_load_ps(s, 0); - Vector4 c1 = _mm_load_ps(s, 56); - Vector4 t0 = (c0 + c1); - Vector4 t7 = (c0 - c1); - - c1 = _mm_load_ps(s, 48); - c0 = _mm_load_ps(s, 8); - Vector4 t1 = (c0 + c1); - Vector4 t6 = (c0 - c1); - - c1 = _mm_load_ps(s, 40); - c0 = _mm_load_ps(s, 16); - Vector4 t2 = (c0 + c1); - Vector4 t5 = (c0 - c1); - - c0 = _mm_load_ps(s, 24); - c1 = _mm_load_ps(s, 32); - Vector4 t3 = (c0 + c1); - Vector4 t4 = (c0 - c1); - - /* - c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; - c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; - c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; - c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; - */ - - c0 = (t0 + t3); - Vector4 c3 = (t0 - t3); - c1 = (t1 + t2); - Vector4 c2 = (t1 - t2); - - /* - c0 = t0 + t3; c3 = t0 - t3; - c1 = t1 + t2; c2 = t1 - t2; - */ - - _mm_store_ps(d, 0, (c0 + c1)); - - _mm_store_ps(d, 32, (c0 - c1)); - - /*y[0] = c0 + c1; - y[4] = c0 - c1;*/ - - Vector4 w0 = new Vector4(0.541196f); - Vector4 w1 = new Vector4(1.306563f); - - _mm_store_ps(d, 16, ((w0 * c2) + (w1 * c3))); - - _mm_store_ps(d, 48, ((w0 * c3) - (w1 * c2))); - /* - y[2] = c2 * r[6] + c3 * r[2]; - y[6] = c3 * r[6] - c2 * r[2]; - */ - - w0 = new Vector4(1.175876f); - w1 = new Vector4(0.785695f); - c3 = ((w0 * t4) + (w1 * t7)); - c0 = ((w0 * t7) - (w1 * t4)); - /* - c3 = t4 * r[3] + t7 * r[5]; - c0 = t7 * r[3] - t4 * r[5]; - */ - - w0 = new Vector4(1.387040f); - w1 = new Vector4(0.275899f); - c2 = ((w0 * t5) + (w1 * t6)); - c1 = ((w0 * t6) - (w1 * t5)); - /* - c2 = t5 * r[1] + t6 * r[7]; - c1 = t6 * r[1] - t5 * r[7]; - */ - - _mm_store_ps(d, 24, (c0 - c2)); - - _mm_store_ps(d, 40, (c3 - c1)); - //y[5] = c3 - c1; y[3] = c0 - c2; - - Vector4 invsqrt2 = new Vector4(0.707107f); - c0 = ((c0 + c2) * invsqrt2); - c3 = ((c3 + c1) * invsqrt2); - //c0 = (c0 + c2) * invsqrt2; - //c3 = (c3 + c1) * invsqrt2; - - _mm_store_ps(d, 8, (c0 + c3)); - - _mm_store_ps(d, 56, (c0 - c3)); - //y[1] = c0 + c3; y[7] = c0 - c3; - - /*for(i = 0;i < 8;i++) - { - y[i] *= invsqrt2h; - }*/ - } - - public static void fDCT8x8_llm_sse(MutableSpan s, MutableSpan d, MutableSpan temp) - { - Transpose8x8(s, temp); - - fDCT2D8x4_32f(temp, d); - - fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - - Transpose8x8(d, temp); - - fDCT2D8x4_32f(temp, d); - - fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - - Vector4 c = new Vector4(0.1250f); - - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//0 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//1 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//2 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//3 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//4 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//5 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//6 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//7 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//8 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//9 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//10 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//11 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//12 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//13 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//14 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//15 - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 _mm_load_ps(MutableSpan src, int offset) - { - src = src.Slice(offset); - return new Vector4(src[0], src[1], src[2], src[3]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void _mm_store_ps(MutableSpan dest, int offset, Vector4 src) - { - dest = dest.Slice(offset); - dest[0] = src.X; - dest[1] = src.Y; - dest[2] = src.Z; - dest[3] = src.W; - } - - private static readonly Vector4 _1_175876 = new Vector4(1.175876f); - - private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); - - private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); - - private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); - - private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); - - private static readonly Vector4 _0_298631 = new Vector4(0.298631f); - - private static readonly Vector4 _2_053120 = new Vector4(2.053120f); - - private static readonly Vector4 _3_072711 = new Vector4(3.072711f); - - private static readonly Vector4 _1_501321 = new Vector4(1.501321f); - - private static readonly Vector4 _0_541196 = new Vector4(0.541196f); - - private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); - - private static readonly Vector4 _0_765367 = new Vector4(0.765367f); - - /// - /// Original: - /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 - /// Does a part of the IDCT job on the given parts of the blocks - /// - /// - /// - internal static void iDCT2D8x4_32f(MutableSpan y, MutableSpan x) - { - /* - float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; - for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } - */ - /* - 0: 1.414214 - 1: 1.387040 - 2: 1.306563 - 3: - 4: 1.000000 - 5: 0.785695 - 6: - 7: 0.275899 - */ - - Vector4 my1 = _mm_load_ps(y, 8); - Vector4 my7 = _mm_load_ps(y, 56); - Vector4 mz0 = my1 + my7; - - Vector4 my3 = _mm_load_ps(y, 24); - Vector4 mz2 = my3 + my7; - Vector4 my5 = _mm_load_ps(y, 40); - Vector4 mz1 = my3 + my5; - Vector4 mz3 = my1 + my5; - - Vector4 mz4 = ((mz0 + mz1) * _1_175876); - //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; - //z4 = (z0 + z1) * r[3]; - - mz2 = mz2 * _1_961571 + mz4; - mz3 = mz3 * _0_390181 + mz4; - mz0 = mz0 * _0_899976; - mz1 = mz1 * _2_562915; - - /* - -0.899976 - -2.562915 - -1.961571 - -0.390181 - z0 = z0 * (-r[3] + r[7]); - z1 = z1 * (-r[3] - r[1]); - z2 = z2 * (-r[3] - r[5]) + z4; - z3 = z3 * (-r[3] + r[5]) + z4;*/ - - Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; - Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; - Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; - Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; - - /* - 0.298631 - 2.053120 - 3.072711 - 1.501321 - b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; - b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; - b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; - b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; - */ - - Vector4 my2 = _mm_load_ps(y, 16); - Vector4 my6 = _mm_load_ps(y, 48); - mz4 = (my2 + my6) * _0_541196; - Vector4 my0 = _mm_load_ps(y, 0); - Vector4 my4 = _mm_load_ps(y, 32); - mz0 = my0 + my4; - mz1 = my0 - my4; - - mz2 = mz4 + my6 * _1_847759; - mz3 = mz4 + my2 * _0_765367; - - my0 = mz0 + mz3; - my3 = mz0 - mz3; - my1 = mz1 + mz2; - my2 = mz1 - mz2; - /* - 1.847759 - 0.765367 - z4 = (y[2] + y[6]) * r[6]; - z0 = y[0] + y[4]; z1 = y[0] - y[4]; - z2 = z4 - y[6] * (r[2] + r[6]); - z3 = z4 + y[2] * (r[2] - r[6]); - a0 = z0 + z3; a3 = z0 - z3; - a1 = z1 + z2; a2 = z1 - z2; - */ - - _mm_store_ps(x, 0, my0 + mb0); - - _mm_store_ps(x, 56, my0 - mb0); - - _mm_store_ps(x, 8, my1 + mb1); - - _mm_store_ps(x, 48, my1 - mb1); - - _mm_store_ps(x, 16, my2 + mb2); - - _mm_store_ps(x, 40, my2 - mb2); - - _mm_store_ps(x, 24, my3 + mb3); - - _mm_store_ps(x, 32, my3 - mb3); - /* - x[0] = a0 + b0; x[7] = a0 - b0; - x[1] = a1 + b1; x[6] = a1 - b1; - x[2] = a2 + b2; x[5] = a2 - b2; - x[3] = a3 + b3; x[4] = a3 - b3; - for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } - */ - } - - /// - /// Copies color values from block to the destination image buffer. - /// - /// - /// - /// - internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan buffer, int stride) - { - fixed (Block8x8F* p = &block) - { - float* b = (float*)p; - - for (int y = 0; y < 8; y++) - { - int y8 = y * 8; - int yStride = y * stride; - - for (int x = 0; x < 8; x++) - { - float c = b[y8 + x]; - - if (c < -128) - { - c = 0; - } - else if (c > 127) - { - c = 255; - } - else - { - c += 128; - } - - buffer[yStride + x] = (byte)c; - } - } - } - } - - internal static void fDCT1Dllm_32f(MutableSpan x, MutableSpan y) - { - float t0, t1, t2, t3, t4, t5, t6, t7; - float c0, c1, c2, c3; - float[] r = new float[8]; - - //for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } - r[0] = 1.414214f; - r[1] = 1.387040f; - r[2] = 1.306563f; - r[3] = 1.175876f; - r[4] = 1.000000f; - r[5] = 0.785695f; - r[6] = 0.541196f; - r[7] = 0.275899f; - - const float invsqrt2 = 0.707107f; //(float)(1.0f / M_SQRT2); - //const float invsqrt2h = 0.353554f; //invsqrt2*0.5f; - - c1 = x[0]; - c2 = x[7]; - t0 = c1 + c2; - t7 = c1 - c2; - c1 = x[1]; - c2 = x[6]; - t1 = c1 + c2; - t6 = c1 - c2; - c1 = x[2]; - c2 = x[5]; - t2 = c1 + c2; - t5 = c1 - c2; - c1 = x[3]; - c2 = x[4]; - t3 = c1 + c2; - t4 = c1 - c2; - - c0 = t0 + t3; - c3 = t0 - t3; - c1 = t1 + t2; - c2 = t1 - t2; - - y[0] = c0 + c1; - y[4] = c0 - c1; - y[2] = c2 * r[6] + c3 * r[2]; - y[6] = c3 * r[6] - c2 * r[2]; - - c3 = t4 * r[3] + t7 * r[5]; - c0 = t7 * r[3] - t4 * r[5]; - c2 = t5 * r[1] + t6 * r[7]; - c1 = t6 * r[1] - t5 * r[7]; - - y[5] = c3 - c1; - y[3] = c0 - c2; - c0 = (c0 + c2) * invsqrt2; - c3 = (c3 + c1) * invsqrt2; - y[1] = c0 + c3; - y[7] = c0 - c3; - } - - internal static void fDCT2D_llm( - MutableSpan s, - MutableSpan d, - MutableSpan temp, - bool downscaleBy8 = false, - bool offsetSourceByNeg128 = false) - { - MutableSpan sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s; - - for (int j = 0; j < 8; j++) - { - fDCT1Dllm_32f(sWorker.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - for (int j = 0; j < 8; j++) - { - fDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - if (downscaleBy8) - { - for (int j = 0; j < 64; j++) - { - d[j] *= 0.125f; - } - } - } - - /// - /// Reference implementation to test . - /// Rounding is done used an integer-based algorithm defined in . - /// - /// The input block - /// The destination block of integers - /// The quantization table - /// Pointer to - public static unsafe void UnZigDivRoundRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr) - { - float* s = (float*)src; - float* q = (float*)qt; - - for (int zig = 0; zig < Block8x8F.ScalarCount; zig++) - { - int a = (int)s[unzigPtr[zig]]; - int b = (int)q[zig]; - - int val = RationalRound(a, b); - dest[zig] = val; - } - } - - /// - /// Rounds a rational number defined as dividend/divisor into an integer - /// - /// The dividend - /// The divisior - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RationalRound(int dividend, int divisor) - { - if (dividend >= 0) - { - return (dividend + (divisor >> 1)) / divisor; - } - - return -((-dividend + (divisor >> 1)) / divisor); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs new file mode 100644 index 0000000000..6b9e98d66d --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs @@ -0,0 +1,36 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public partial class ReferenceImplementationsTests + { + public class AccurateDCT : JpegFixture + { + public AccurateDCT(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData(42)] + [InlineData(1)] + [InlineData(2)] + public void ForwardThenInverse(int seed) + { + float[] data = JpegFixture.Create8x8RandomFloatData(-1000, 1000, seed); + + var b0 = default(Block8x8F); + b0.LoadFrom(data); + + Block8x8F b1 = ReferenceImplementations.AccurateDCT.TransformFDCT(ref b0); + Block8x8F b2 = ReferenceImplementations.AccurateDCT.TransformIDCT(ref b1); + + this.CompareBlocks(b0, b2, 1e-4f); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs new file mode 100644 index 0000000000..1fc47726b5 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -0,0 +1,123 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public partial class ReferenceImplementationsTests + { + public class FastFloatingPointDCT : JpegFixture + { + public FastFloatingPointDCT(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData(42, 0)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public void LLM_ForwardThenInverse(int seed, int startAt) + { + int[] data = JpegFixture.Create8x8RandomIntData(-1000, 1000, seed); + float[] original = data.ConvertAllToFloat(); + float[] src = data.ConvertAllToFloat(); + float[] dest = new float[64]; + float[] temp = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, dest, temp, true); + ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D_llm(dest, src, temp); + + this.CompareBlocks(original, src, 0.1f); + } + + // [Fact] + public void LLM_CalcConstants() + { + ReferenceImplementations.LLM_FloatingPoint_DCT.PrintConstants(this.Output); + } + + [Theory] + [InlineData(42, 1000)] + [InlineData(1, 1000)] + [InlineData(2, 1000)] + [InlineData(42, 200)] + [InlineData(1, 200)] + [InlineData(2, 200)] + public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) + { + float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-range, range, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + + Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); + + this.CompareBlocks(expected, actual, 0.1f); + } + + [Theory] + [InlineData(42, 1000)] + [InlineData(1, 1000)] + [InlineData(2, 1000)] + public void LLM_IDCT_CompareToIntegerRoundedAccurateImplementation(int seed, int range) + { + Block8x8F fSource = CreateRoundedRandomFloatBlock(-range, range, seed); + Block8x8 iSource = fSource.RoundAsInt16Block(); + + Block8x8 iExpected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref iSource); + Block8x8F fExpected = iExpected.AsFloatBlock(); + + Block8x8F fActual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref fSource); + + this.CompareBlocks(fExpected, fActual, 2); + } + + + [Theory] + [InlineData(42)] + [InlineData(1)] + [InlineData(2)] + public void LLM_FDCT_IsEquivalentTo_AccurateImplementation(int seed) + { + float[] floatData = JpegFixture.Create8x8RandomFloatData(-1000, 1000); + + Block8x8F source = default(Block8x8F); + source.LoadFrom(floatData); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); + Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformFDCT_UpscaleBy8(ref source); + actual /= 8; + + this.CompareBlocks(expected, actual, 1f); + } + + [Theory] + [InlineData(42, 1000)] + [InlineData(1, 1000)] + [InlineData(2, 1000)] + [InlineData(42, 200)] + [InlineData(1, 200)] + [InlineData(2, 200)] + public void GT_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) + { + int[] intData = JpegFixture.Create8x8RandomIntData(-range, range, seed); + float[] floatSrc = intData.ConvertAllToFloat(); + + ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData); + + float[] dest = new float[64]; + + ReferenceImplementations.GT_FloatingPoint_DCT.iDCT8x8GT(floatSrc, dest); + + this.CompareBlocks(intData.ConvertAllToFloat(), dest, 1f); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs new file mode 100644 index 0000000000..f384a76c48 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs @@ -0,0 +1,90 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public partial class ReferenceImplementationsTests + { + public class StandardIntegerDCT : JpegFixture + { + public StandardIntegerDCT(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData(42, 200)] + [InlineData(1, 200)] + [InlineData(2, 200)] + public void IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) + { + int[] data = Create8x8RandomIntData(-range, range, seed); + + Block8x8 source = default(Block8x8); + source.LoadFrom(data); + + Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + Block8x8 actual = ReferenceImplementations.StandardIntegerDCT.TransformIDCT(ref source); + + this.CompareBlocks(expected, actual, 1); + } + + [Theory] + [InlineData(42)] + [InlineData(1)] + [InlineData(2)] + public void FDCT_IsEquivalentTo_AccurateImplementation(int seed) + { + int[] data = Create8x8RandomIntData(-1000, 1000, seed); + + Block8x8F source = default(Block8x8F); + source.LoadFrom(data); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); + + source += 128; + Block8x8 temp = source.RoundAsInt16Block(); + Block8x8 actual8 = ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8(ref temp); + Block8x8F actual = actual8.AsFloatBlock(); + actual /= 8; + + this.CompareBlocks(expected, actual, 1f); + } + + + [Theory] + [InlineData(42, 0)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public void ForwardThenInverse(int seed, int startAt) + { + Span original = JpegFixture.Create8x8RandomIntData(-200, 200, seed); + + Span block = original.AddScalarToAllValues(128); + + ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8_Inplace(block); + + for (int i = 0; i < 64; i++) + { + block[i] /= 8; + } + + ReferenceImplementations.StandardIntegerDCT.TransformIDCTInplace(block); + + for (int i = startAt; i < 64; i++) + { + float expected = original[i]; + float actual = (float)block[i]; + + Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs index 50b94bc24c..26ec454f91 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs @@ -1,124 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; + +using Xunit.Abstractions; // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Formats.Jpg +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using ImageSharp.Formats.Jpg; - - using Xunit; - using Xunit.Abstractions; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; - public class ReferenceImplementationsTests : JpegUtilityTestFixture + public partial class ReferenceImplementationsTests : JpegFixture { public ReferenceImplementationsTests(ITestOutputHelper output) : base(output) { } - - - [Theory] - [InlineData(42)] - [InlineData(1)] - [InlineData(2)] - public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) - { - MutableSpan intData = Create8x8RandomIntData(-200, 200, seed); - MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan(); - - ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData); - - MutableSpan dest = new MutableSpan(64); - MutableSpan temp = new MutableSpan(64); - - ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp); - - for (int i = 0; i < 64; i++) - { - float expected = intData[i]; - float actual = dest[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); - } - } - - [Theory] - [InlineData(42, 0)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public void IntegerDCT_ForwardThenInverse(int seed, int startAt) - { - MutableSpan original = Create8x8RandomIntData(-200, 200, seed); - - MutableSpan block = original.AddScalarToAllValues(128); - - ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block); - - for (int i = 0; i < 64; i++) - { - block[i] /= 8; - } - - ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block); - - for (int i = startAt; i < 64; i++) - { - float expected = original[i]; - float actual = (float)block[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); - } - - } - - [Theory] - [InlineData(42, 0)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt) - { - int[] data = Create8x8RandomIntData(-200, 200, seed); - MutableSpan src = new MutableSpan(data).ConvertToFloat32MutableSpan(); - MutableSpan dest = new MutableSpan(64); - MutableSpan temp = new MutableSpan(64); - - ReferenceImplementations.fDCT2D_llm(src, dest, temp, true); - ReferenceImplementations.iDCT2D_llm(dest, src, temp); - - for (int i = startAt; i < 64; i++) - { - float expected = data[i]; - float actual = (float)src[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); - } - } - - [Theory] - [InlineData(42)] - [InlineData(1)] - [InlineData(2)] - public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) - { - MutableSpan intData = Create8x8RandomIntData(-200, 200, seed); - MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan(); - - ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData); - - MutableSpan dest = new MutableSpan(64); - MutableSpan temp = new MutableSpan(64); - - ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); - - for (int i = 0; i < 64; i++) - { - float expected = intData[i]; - float actual = dest[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs new file mode 100644 index 0000000000..6e68c43f21 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -0,0 +1,168 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + using System.IO; + using System.Linq; + + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public class SpectralJpegTests + { + public SpectralJpegTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + public static readonly string[] BaselineTestJpegs = + { + TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.Bad.ExifUndefType, + }; + + public static readonly string[] ProgressiveTestJpegs = + { + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + }; + + public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray(); + + [Theory] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void PdfJsDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + where TPixel : struct, IPixel + { + PdfJsJpegDecoderCore decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms); + + var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + VerifyJpeg.SaveSpectralImage(provider, data); + } + } + + [Theory] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void OriginalDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + where TPixel : struct, IPixel + { + OrigJpegDecoderCore decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms, false); + + var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + VerifyJpeg.SaveSpectralImage(provider, data); + } + } + + private void VerifySpectralCorrectness( + TestImageProvider provider, + LibJpegTools.SpectralData imageSharpData) + where TPixel : struct, IPixel + { + var libJpegData = LibJpegTools.ExtractSpectralData(provider.SourceFileOrDescription); + + bool equality = libJpegData.Equals(imageSharpData); + this.Output.WriteLine("Spectral data equality: " + equality); + + int componentCount = imageSharpData.ComponentCount; + if (libJpegData.ComponentCount != componentCount) + { + throw new Exception("libJpegData.ComponentCount != componentCount"); + } + + double averageDifference = 0; + double totalDifference = 0; + double tolerance = 0; + + this.Output.WriteLine("*** Differences ***"); + for (int i = 0; i < componentCount; i++) + { + LibJpegTools.ComponentData libJpegComponent = libJpegData.Components[i]; + LibJpegTools.ComponentData imageSharpComponent = imageSharpData.Components[i]; + + (double total, double average) diff = LibJpegTools.CalculateDifference(libJpegComponent, imageSharpComponent); + + this.Output.WriteLine($"Component{i}: {diff}"); + averageDifference += diff.average; + totalDifference += diff.total; + tolerance += libJpegComponent.SpectralBlocks.Length; + } + averageDifference /= componentCount; + + tolerance /= 64; // fair enough? + + this.Output.WriteLine($"AVERAGE: {averageDifference}"); + this.Output.WriteLine($"TOTAL: {totalDifference}"); + this.Output.WriteLine($"TOLERANCE = totalNumOfBlocks / 64 = {tolerance}"); + + Assert.True(totalDifference < tolerance); + } + + [Theory(Skip = "Debug/Comparison only")] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void VerifySpectralCorrectness_PdfJs(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.IsWindows) + { + return; + } + + PdfJsJpegDecoderCore decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms); + var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + + this.VerifySpectralCorrectness(provider, imageSharpData); + } + } + + [Theory] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void VerifySpectralResults_OriginalDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.IsWindows) + { + return; + } + + OrigJpegDecoderCore decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms); + var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + + this.VerifySpectralCorrectness(provider, imageSharpData); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs new file mode 100644 index 0000000000..07268ef214 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -0,0 +1,190 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + + +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Text; + + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + + using Xunit; + using Xunit.Abstractions; + + public class JpegFixture : MeasureFixture + { + public JpegFixture(ITestOutputHelper output) : base(output) + { + } + + // ReSharper disable once InconsistentNaming + public static float[] Create8x8FloatData() + { + float[] result = new float[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = i * 10 + j; + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static int[] Create8x8IntData() + { + int[] result = new int[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = i * 10 + j; + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static short[] Create8x8ShortData() + { + short[] result = new short[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = (short)(i * 10 + j); + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) + { + Random rnd = new Random(seed); + int[] result = new int[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = rnd.Next(minValue, maxValue); + } + } + return result; + } + + internal static float[] Create8x8RoundedRandomFloatData(int minValue, int maxValue, int seed = 42) + => Create8x8RandomIntData(minValue, maxValue, seed).ConvertAllToFloat(); + + public static float[] Create8x8RandomFloatData(float minValue, float maxValue, int seed = 42) + { + Random rnd = new Random(seed); + float[] result = new float[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + double val = rnd.NextDouble(); + val *= maxValue - minValue; + val += minValue; + + result[i * 8 + j] = (float)val; + } + } + return result; + } + + internal static Block8x8F CreateRandomFloatBlock(float minValue, float maxValue, int seed = 42) => + Block8x8F.Load(Create8x8RandomFloatData(minValue, maxValue, seed)); + + internal static Block8x8F CreateRoundedRandomFloatBlock(int minValue, int maxValue, int seed = 42) => + Block8x8F.Load(Create8x8RoundedRandomFloatData(minValue, maxValue, seed)); + + internal void Print8x8Data(T[] data) => this.Print8x8Data(new Span(data)); + + internal void Print8x8Data(Span data) + { + StringBuilder bld = new StringBuilder(); + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + bld.Append($"{data[i * 8 + j],3} "); + } + bld.AppendLine(); + } + + this.Output.WriteLine(bld.ToString()); + } + + internal void PrintLinearData(T[] data) => this.PrintLinearData(new Span(data), data.Length); + + internal void PrintLinearData(Span data, int count = -1) + { + if (count < 0) count = data.Length; + + StringBuilder bld = new StringBuilder(); + for (int i = 0; i < count; i++) + { + bld.Append($"{data[i],3} "); + } + this.Output.WriteLine(bld.ToString()); + } + + protected void Print(string msg) + { + Debug.WriteLine(msg); + this.Output.WriteLine(msg); + } + + internal void CompareBlocks(Block8x8 a, Block8x8 b, int tolerance) => + this.CompareBlocks(a.AsFloatBlock(), b.AsFloatBlock(), (float)tolerance + 1e-5f); + + internal void CompareBlocks(Block8x8F a, Block8x8F b, float tolerance) + => this.CompareBlocks(a.ToArray(), b.ToArray(), tolerance); + + internal void CompareBlocks(Span a, Span b, float tolerance) + { + ApproximateFloatComparer comparer = new ApproximateFloatComparer(tolerance); + double totalDifference = 0.0; + + bool failed = false; + + for (int i = 0; i < 64; i++) + { + float expected = a[i]; + float actual = b[i]; + totalDifference += Math.Abs(expected - actual); + + if (!comparer.Equals(expected, actual)) + { + failed = true; + this.Output.WriteLine($"Difference too large at index {i}"); + } + } + + this.Output.WriteLine("TOTAL DIFF: "+totalDifference); + Assert.False(failed); + } + + internal static OrigJpegDecoderCore ParseStream(string testFileName, bool metaDataOnly = false) + { + byte[] bytes = TestFile.Create(testFileName).Bytes; + using (var ms = new MemoryStream(bytes)) + { + var decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder()); + decoder.ParseStream(ms, metaDataOnly); + return decoder; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs new file mode 100644 index 0000000000..40b41b9cba --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -0,0 +1,196 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Linq; + using System.Numerics; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + using SixLabors.ImageSharp.Memory; + using SixLabors.Primitives; + + internal static partial class LibJpegTools + { + /// + /// Stores spectral blocks for jpeg components. + /// + public class ComponentData : IEquatable, IJpegComponent + { + public ComponentData(int widthInBlocks, int heightInBlocks, int index) + { + this.HeightInBlocks = heightInBlocks; + this.WidthInBlocks = widthInBlocks; + this.Index = index; + this.SpectralBlocks = new Buffer2D(this.WidthInBlocks, this.HeightInBlocks); + } + + public Size Size => new Size(this.WidthInBlocks, this.HeightInBlocks); + + public int Index { get; } + + public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks); + + public Size SamplingFactors => throw new NotSupportedException(); + + public Size SubSamplingDivisors => throw new NotSupportedException(); + + public int HeightInBlocks { get; } + + public int WidthInBlocks { get; } + + public int QuantizationTableIndex => throw new NotSupportedException(); + + public Buffer2D SpectralBlocks { get; private set; } + + public short MinVal { get; private set; } = short.MaxValue; + + public short MaxVal { get; private set; } = short.MinValue; + + internal void MakeBlock(short[] data, int y, int x) + { + this.MinVal = Math.Min((short)this.MinVal, data.Min()); + this.MaxVal = Math.Max((short)this.MaxVal, data.Max()); + this.SpectralBlocks[x, y] = new Block8x8(data); + } + + public static ComponentData Load(PdfJsFrameComponent c, int index) + { + var result = new ComponentData( + c.WidthInBlocks, + c.HeightInBlocks, + index + ); + + for (int y = 0; y < result.HeightInBlocks; y++) + { + for (int x = 0; x < result.WidthInBlocks; x++) + { + short[] data = c.GetBlockBuffer(y, x).ToArray(); + result.MakeBlock(data, y, x); + } + } + + return result; + } + + public static ComponentData Load(OrigComponent c) + { + var result = new ComponentData( + c.SizeInBlocks.Width, + c.SizeInBlocks.Height, + c.Index + ); + + for (int y = 0; y < result.HeightInBlocks; y++) + { + for (int x = 0; x < result.WidthInBlocks; x++) + { + short[] data = c.GetBlockReference(x, y).ToArray(); + result.MakeBlock(data, y, x); + } + } + + return result; + } + + public Image CreateGrayScaleImage() + { + Image result = new Image(this.WidthInBlocks * 8, this.HeightInBlocks * 8); + + for (int by = 0; by < this.HeightInBlocks; by++) + { + for (int bx = 0; bx < this.WidthInBlocks; bx++) + { + this.WriteToImage(bx, by, result); + } + } + return result; + } + + internal void WriteToImage(int bx, int by, Image image) + { + Block8x8 block = this.SpectralBlocks[bx, by]; + + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + var val = this.GetBlockValue(block, x, y); + + Vector4 v = new Vector4(val, val, val, 1); + Rgba32 color = default(Rgba32); + color.PackFromVector4(v); + + int yy = by * 8 + y; + int xx = bx * 8 + x; + image[xx, yy] = color; + } + } + } + + internal float GetBlockValue(Block8x8 block, int x, int y) + { + float d = (this.MaxVal - this.MinVal); + float val = block[y, x]; + val -= this.MinVal; + val /= d; + return val; + } + + public bool Equals(ComponentData other) + { + if (Object.ReferenceEquals(null, other)) return false; + if (Object.ReferenceEquals(this, other)) return true; + bool ok = this.Index == other.Index && this.HeightInBlocks == other.HeightInBlocks + && this.WidthInBlocks == other.WidthInBlocks; + //&& this.MinVal == other.MinVal + //&& this.MaxVal == other.MaxVal; + if (!ok) return false; + + for (int y = 0; y < this.HeightInBlocks; y++) + { + for (int x = 0; x < this.WidthInBlocks; x++) + { + Block8x8 a = this.SpectralBlocks[x, y]; + Block8x8 b = other.SpectralBlocks[x, y]; + if (!a.Equals(b)) return false; + } + } + return true; + } + + public override bool Equals(object obj) + { + if (Object.ReferenceEquals(null, obj)) return false; + if (Object.ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return this.Equals((ComponentData)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = this.Index; + hashCode = (hashCode * 397) ^ this.HeightInBlocks; + hashCode = (hashCode * 397) ^ this.WidthInBlocks; + hashCode = (hashCode * 397) ^ this.MinVal.GetHashCode(); + hashCode = (hashCode * 397) ^ this.MaxVal.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(ComponentData left, ComponentData right) + { + return Object.Equals(left, right); + } + + public static bool operator !=(ComponentData left, ComponentData right) + { + return !Object.Equals(left, right); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs new file mode 100644 index 0000000000..ae7a9c046f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -0,0 +1,149 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Linq; + using System.Numerics; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + + internal static partial class LibJpegTools + { + /// + /// Stores spectral jpeg compoent data in libjpeg-compatible style. + /// + public class SpectralData : IEquatable + { + public int ComponentCount { get; private set; } + + public LibJpegTools.ComponentData[] Components { get; private set; } + + internal SpectralData(LibJpegTools.ComponentData[] components) + { + this.ComponentCount = components.Length; + this.Components = components; + } + + public static SpectralData LoadFromImageSharpDecoder(PdfJsJpegDecoderCore decoder) + { + PdfJsFrameComponent[] srcComponents = decoder.Frame.Components; + LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); + + return new SpectralData(destComponents); + } + + public static SpectralData LoadFromImageSharpDecoder(OrigJpegDecoderCore decoder) + { + OrigComponent[] srcComponents = decoder.Components; + LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); + + return new SpectralData(destComponents); + } + + public Image TryCreateRGBSpectralImage() + { + if (this.ComponentCount != 3) return null; + + LibJpegTools.ComponentData c0 = this.Components[0]; + LibJpegTools.ComponentData c1 = this.Components[1]; + LibJpegTools.ComponentData c2 = this.Components[2]; + + if (c0.Size != c1.Size || c1.Size != c2.Size) + { + return null; + } + + Image result = new Image(c0.WidthInBlocks * 8, c0.HeightInBlocks * 8); + + for (int by = 0; by < c0.HeightInBlocks; by++) + { + for (int bx = 0; bx < c0.WidthInBlocks; bx++) + { + this.WriteToImage(bx, by, result); + } + } + return result; + } + + internal void WriteToImage(int bx, int by, Image image) + { + LibJpegTools.ComponentData c0 = this.Components[0]; + LibJpegTools.ComponentData c1 = this.Components[1]; + LibJpegTools.ComponentData c2 = this.Components[2]; + + Block8x8 block0 = c0.SpectralBlocks[bx, by]; + Block8x8 block1 = c1.SpectralBlocks[bx, by]; + Block8x8 block2 = c2.SpectralBlocks[bx, by]; + + float d0 = (c0.MaxVal - c0.MinVal); + float d1 = (c1.MaxVal - c1.MinVal); + float d2 = (c2.MaxVal - c2.MinVal); + + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + float val0 = c0.GetBlockValue(block0, x, y); + float val1 = c0.GetBlockValue(block1, x, y); + float val2 = c0.GetBlockValue(block2, x, y); + + Vector4 v = new Vector4(val0, val1, val2, 1); + Rgba32 color = default(Rgba32); + color.PackFromVector4(v); + + int yy = by * 8 + y; + int xx = bx * 8 + x; + image[xx, yy] = color; + } + } + } + + public bool Equals(SpectralData other) + { + if (Object.ReferenceEquals(null, other)) return false; + if (Object.ReferenceEquals(this, other)) return true; + if (this.ComponentCount != other.ComponentCount) + { + return false; + } + + for (int i = 0; i < this.ComponentCount; i++) + { + LibJpegTools.ComponentData a = this.Components[i]; + LibJpegTools.ComponentData b = other.Components[i]; + if (!a.Equals(b)) return false; + } + return true; + } + + public override bool Equals(object obj) + { + if (Object.ReferenceEquals(null, obj)) return false; + if (Object.ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return this.Equals((SpectralData)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (this.ComponentCount * 397) ^ (this.Components != null ? this.Components[0].GetHashCode() : 0); + } + } + + public static bool operator ==(SpectralData left, SpectralData right) + { + return Object.Equals(left, right); + } + + public static bool operator !=(SpectralData left, SpectralData right) + { + return !Object.Equals(left, right); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs new file mode 100644 index 0000000000..5875110202 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs @@ -0,0 +1,133 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Numerics; + using System.Reflection; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + /// + /// Utilities to read raw libjpeg data for reference conversion. + /// + internal static partial class LibJpegTools + { + public static (double total, double average) CalculateDifference(ComponentData expected, ComponentData actual) + { + BigInteger totalDiff = 0; + if (actual.WidthInBlocks < expected.WidthInBlocks) + { + throw new Exception("actual.WidthInBlocks < expected.WidthInBlocks"); + } + + if (actual.HeightInBlocks < expected.HeightInBlocks) + { + throw new Exception("actual.HeightInBlocks < expected.HeightInBlocks"); + } + + int w = expected.WidthInBlocks; + int h = expected.HeightInBlocks; + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + Block8x8 aa = expected.SpectralBlocks[x, y]; + Block8x8 bb = actual.SpectralBlocks[x, y]; + + long diff = Block8x8.TotalDifference(ref aa, ref bb); + totalDiff += diff; + } + } + + int count = w * h; + double total = (double)totalDiff; + double average = (double)totalDiff / (count * Block8x8.Size); + return (total, average); + } + + private static string DumpToolFullPath => Path.Combine( + TestEnvironment.ToolsDirectoryFullPath, + @"jpeg\dump-jpeg-coeffs.exe"); + + /// + /// Executes 'dump-jpeg-coeffs.exe' for the given jpeg image file, saving the libjpeg spectral data into 'destFile'. Windows only! + /// See: + /// + /// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md + /// + /// + public static void RunDumpJpegCoeffsTool(string sourceFile, string destFile) + { + if (!TestEnvironment.IsWindows) + { + throw new InvalidOperationException("Can't run dump-jpeg-coeffs.exe in non-Windows environment. Skip this test on Linux/Unix!"); + } + + string args = $@"""{sourceFile}"" ""{destFile}"""; + var process = Process.Start(DumpToolFullPath, args); + process.WaitForExit(); + } + + /// + /// Extract libjpeg from the given jpg file with 'dump-jpeg-coeffs.exe'. Windows only! + /// See: + /// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md + /// + public static SpectralData ExtractSpectralData(string inputFile) + { + TestFile testFile = TestFile.Create(inputFile); + + string outDir = TestEnvironment.CreateOutputDirectory(".Temp", $"JpegCoeffs"); + string fn = $"{Path.GetFileName(inputFile)}-{new Random().Next(1000)}.dctcoeffs"; + string coeffFileFullPath = Path.Combine(outDir, fn); + + try + { + RunDumpJpegCoeffsTool(testFile.FullPath, coeffFileFullPath); + + using (var dumpStream = new FileStream(coeffFileFullPath, FileMode.Open)) + using (var rdr = new BinaryReader(dumpStream)) + { + int componentCount = rdr.ReadInt16(); + ComponentData[] result = new ComponentData[componentCount]; + + for (int i = 0; i < componentCount; i++) + { + int widthInBlocks = rdr.ReadInt16(); + int heightInBlocks = rdr.ReadInt16(); + ComponentData resultComponent = new ComponentData(widthInBlocks, heightInBlocks, i); + result[i] = resultComponent; + } + + byte[] buffer = new byte[64*sizeof(short)]; + + for (int i = 0; i < result.Length; i++) + { + ComponentData c = result[i]; + + for (int y = 0; y < c.HeightInBlocks; y++) + { + for (int x = 0; x < c.WidthInBlocks; x++) + { + rdr.Read(buffer, 0, buffer.Length); + + short[] block = buffer.AsSpan().NonPortableCast().ToArray(); + c.MakeBlock(block, y, x); + } + } + } + + return new SpectralData(result); + } + } + finally + { + if (File.Exists(coeffFileFullPath)) + { + File.Delete(coeffFileFullPath); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs new file mode 100644 index 0000000000..6a1e09a9b1 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs @@ -0,0 +1,122 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + internal static partial class ReferenceImplementations + { + /// + /// True accurate FDCT/IDCT implementations. We should test everything against them! + /// Based on: + /// https://github.com/keithw/mympeg2enc/blob/master/idct.c#L222 + /// Claiming: + /// /* reference idct taken from "ieeetest.c" + /// * Written by Tom Lane (tgl@cs.cmu.edu). + /// * Released to public domain 11/22/93. + /// */ + /// + internal static class AccurateDCT + { + private static double[,] CosLut = InitCosLut(); + + public static Block8x8 TransformIDCT(ref Block8x8 block) + { + Block8x8F temp = block.AsFloatBlock(); + Block8x8F res0 = TransformIDCT(ref temp); + return res0.RoundAsInt16Block(); + } + + public static void TransformIDCTInplace(Span span) + { + var temp = new Block8x8(); + temp.LoadFrom(span); + Block8x8 result = TransformIDCT(ref temp); + result.CopyTo(span); + } + + public static Block8x8 TransformFDCT(ref Block8x8 block) + { + Block8x8F temp = block.AsFloatBlock(); + Block8x8F res0 = TransformFDCT(ref temp); + return res0.RoundAsInt16Block(); + } + + public static void TransformFDCTInplace(Span span) + { + var temp = new Block8x8(); + temp.LoadFrom(span); + Block8x8 result = TransformFDCT(ref temp); + result.CopyTo(span); + } + + public static Block8x8F TransformIDCT(ref Block8x8F block) + { + int x, y, u, v; + double tmp, tmp2; + Block8x8F res = default(Block8x8F); + + for (y=0; y<8; y++) { + for (x=0; x<8; x++) { + tmp = 0.0; + for (v=0; v<8; v++) { + tmp2 = 0.0; + for (u=0; u<8; u++) { + tmp2 += (double) block[v * 8 + u] * CosLut[x, u]; + } + tmp += CosLut[y, v] * tmp2; + } + res[y * 8 + x] = (float)tmp; + } + } + return res; + } + + public static Block8x8F TransformFDCT(ref Block8x8F block) + { + int x, y, u, v; + double tmp, tmp2; + Block8x8F res = default(Block8x8F); + + for (v = 0; v < 8; v++) + { + for (u = 0; u < 8; u++) + { + tmp = 0.0; + for (y = 0; y < 8; y++) + { + tmp2 = 0.0; + for (x = 0; x < 8; x++) + { + tmp2 += (double)block[y * 8 + x] * CosLut[x,u]; + } + tmp += CosLut[y, v] * tmp2; + } + res[v * 8 + u] = (float) tmp; + } + } + + return res; + } + + private static double[,] InitCosLut() + { + double[,] coslu = new double[8, 8]; + int a, b; + double tmp; + + for (a = 0; a < 8; a++) + for (b = 0; b < 8; b++) + { + tmp = Math.Cos((double)((a + a + 1) * b) * (3.14159265358979323846 / 16.0)); + if (b == 0) + { + tmp /= Math.Sqrt(2.0); + } + coslu[a, b] = tmp * 0.5; + } + return coslu; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.GT_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.GT_FloatingPoint_DCT.cs new file mode 100644 index 0000000000..2e2f12fbcd --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.GT_FloatingPoint_DCT.cs @@ -0,0 +1,69 @@ +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + + internal static partial class ReferenceImplementations + { + /// + /// Non-optimized method ported from: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L446 + /// + /// *** Paper *** + /// Plonka, Gerlind, and Manfred Tasche. "Fast and numerically stable algorithms for discrete cosine transforms." Linear algebra and its applications 394 (2005) : 309 - 345. + /// + internal static class GT_FloatingPoint_DCT + { + public static void idct81d_GT(Span src, Span dst) + { + for (int i = 0; i < 8; i++) + { + float mx00 = 1.4142135623731f * src[0]; + float mx01 = 1.38703984532215f * src[1] + 0.275899379282943f * src[7]; + float mx02 = 1.30656296487638f * src[2] + 0.541196100146197f * src[6]; + float mx03 = 1.17587560241936f * src[3] + 0.785694958387102f * src[5]; + float mx04 = 1.4142135623731f * src[4]; + float mx05 = -0.785694958387102f * src[3] + 1.17587560241936f * src[5]; + float mx06 = 0.541196100146197f * src[2] - 1.30656296487638f * src[6]; + float mx07 = -0.275899379282943f * src[1] + 1.38703984532215f * src[7]; + float mx09 = mx00 + mx04; + float mx0a = mx01 + mx03; + float mx0b = 1.4142135623731f * mx02; + float mx0c = mx00 - mx04; + float mx0d = mx01 - mx03; + float mx0e = 0.353553390593274f * (mx09 - mx0b); + float mx0f = 0.353553390593274f * (mx0c + mx0d); + float mx10 = 0.353553390593274f * (mx0c - mx0d); + float mx11 = 1.4142135623731f * mx06; + float mx12 = mx05 + mx07; + float mx13 = mx05 - mx07; + float mx14 = 0.353553390593274f * (mx11 + mx12); + float mx15 = 0.353553390593274f * (mx11 - mx12); + float mx16 = 0.5f * mx13; + dst[0] = 0.25f * (mx09 + mx0b) + 0.353553390593274f * mx0a; + dst[1] = 0.707106781186547f * (mx0f + mx15); + dst[2] = 0.707106781186547f * (mx0f - mx15); + dst[3] = 0.707106781186547f * (mx0e + mx16); + dst[4] = 0.707106781186547f * (mx0e - mx16); + dst[5] = 0.707106781186547f * (mx10 - mx14); + dst[6] = 0.707106781186547f * (mx10 + mx14); + dst[7] = 0.25f * (mx09 + mx0b) - 0.353553390593274f * mx0a; + dst = dst.Slice(8); + src = src.Slice(8); + } + } + + public static void iDCT8x8GT(Span s, Span d) + { + idct81d_GT(s, d); + + Transpose8x8(d); + + idct81d_GT(d, d); + + Transpose8x8(d); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs new file mode 100644 index 0000000000..ef9a73d12d --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -0,0 +1,558 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; + + using Xunit.Abstractions; + + internal static partial class ReferenceImplementations + { + /// + /// Contains port of non-optimized methods in: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp + /// + /// *** Paper *** + /// paper LLM89 + /// C. Loeffler, A. Ligtenberg, and G. S. Moschytz, + /// "Practical fast 1-D DCT algorithms with 11 multiplications," + /// Proc. Int'l. Conf. on Acoustics, Speech, and Signal Processing (ICASSP89), pp. 988-991, 1989. + /// + /// The main purpose of this code is testing and documentation, it is intented to be similar to it's original counterpart. + /// DO NOT clean it! + /// DO NOT StyleCop it! + /// + internal static class LLM_FloatingPoint_DCT + { + public static Block8x8F TransformIDCT(ref Block8x8F source) + { + float[] s = new float[64]; + source.CopyTo(s); + float[] d = new float[64]; + float[] temp = new float[64]; + + iDCT2D_llm(s, d, temp); + Block8x8F result = default(Block8x8F); + result.LoadFrom(d); + return result; + } + + public static Block8x8F TransformFDCT_UpscaleBy8(ref Block8x8F source) + { + float[] s = new float[64]; + source.CopyTo(s); + float[] d = new float[64]; + float[] temp = new float[64]; + + fDCT2D_llm(s, d, temp); + Block8x8F result = default(Block8x8F); + result.LoadFrom(d); + return result; + } + + private static double cos(double x) => Math.Cos(x); + + private const double M_PI = Math.PI; + + private static readonly double M_SQRT2 = Math.Sqrt(2); + + public static float[] PrintConstants(ITestOutputHelper output) + { + float[] r = new float[8]; + for (int i = 0; i < 8; i++) + { + r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); + output?.WriteLine($"float r{i} = {r[i]:R}f;"); + } + return r; + } + +#pragma warning disable 219 + + /// + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200 + /// + /// + /// + private static void iDCT1Dllm_32f(Span y, Span x) + { + float a0, a1, a2, a3, b0, b1, b2, b3; + float z0, z1, z2, z3, z4; + + // see: PrintConstants() + + float r0 = 1.41421354f; + float r1 = 1.3870399f; + float r2 = 1.306563f; + float r3 = 1.17587554f; + float r4 = 1f; + float r5 = 0.785694957f; + float r6 = 0.5411961f; + float r7 = 0.27589938f; + + z0 = y[1] + y[7]; + z1 = y[3] + y[5]; + z2 = y[3] + y[7]; + z3 = y[1] + y[5]; + z4 = (z0 + z1) * r3; + + z0 = z0 * (-r3 + r7); + z1 = z1 * (-r3 - r1); + z2 = z2 * (-r3 - r5) + z4; + z3 = z3 * (-r3 + r5) + z4; + + b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2; + b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3; + b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2; + b0 = y[1] * (r1 + r3 - r5 - r7) + z0 + z3; + + z4 = (y[2] + y[6]) * r6; + z0 = y[0] + y[4]; + z1 = y[0] - y[4]; + z2 = z4 - y[6] * (r2 + r6); + z3 = z4 + y[2] * (r2 - r6); + a0 = z0 + z3; + a3 = z0 - z3; + a1 = z1 + z2; + a2 = z1 - z2; + + x[0] = a0 + b0; + x[7] = a0 - b0; + x[1] = a1 + b1; + x[6] = a1 - b1; + x[2] = a2 + b2; + x[5] = a2 - b2; + x[3] = a3 + b3; + x[4] = a3 - b3; + } + + /// + /// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 + /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp" + /// + /// + /// + /// + internal static void iDCT2D_llm(Span s, Span d, Span temp) + { + int j; + + for (j = 0; j < 8; j++) + { + iDCT1Dllm_32f(s.Slice(j * 8), temp.Slice(j * 8)); + } + + Transpose8x8(temp, d); + + for (j = 0; j < 8; j++) + { + iDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); + } + + Transpose8x8(temp, d); + + for (j = 0; j < 64; j++) + { + d[j] *= 0.125f; + } + } + + /// + /// Original: + /// + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15 + /// + /// + /// Source + /// Destination + public static void fDCT2D8x4_32f(Span s, Span d) + { + Vector4 c0 = _mm_load_ps(s, 0); + Vector4 c1 = _mm_load_ps(s, 56); + Vector4 t0 = (c0 + c1); + Vector4 t7 = (c0 - c1); + + c1 = _mm_load_ps(s, 48); + c0 = _mm_load_ps(s, 8); + Vector4 t1 = (c0 + c1); + Vector4 t6 = (c0 - c1); + + c1 = _mm_load_ps(s, 40); + c0 = _mm_load_ps(s, 16); + Vector4 t2 = (c0 + c1); + Vector4 t5 = (c0 - c1); + + c0 = _mm_load_ps(s, 24); + c1 = _mm_load_ps(s, 32); + Vector4 t3 = (c0 + c1); + Vector4 t4 = (c0 - c1); + + /* + c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; + c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; + c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; + c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; + */ + + c0 = (t0 + t3); + Vector4 c3 = (t0 - t3); + c1 = (t1 + t2); + Vector4 c2 = (t1 - t2); + + /* + c0 = t0 + t3; c3 = t0 - t3; + c1 = t1 + t2; c2 = t1 - t2; + */ + + _mm_store_ps(d, 0, (c0 + c1)); + + _mm_store_ps(d, 32, (c0 - c1)); + + /*y[0] = c0 + c1; + y[4] = c0 - c1;*/ + + Vector4 w0 = new Vector4(0.541196f); + Vector4 w1 = new Vector4(1.306563f); + + _mm_store_ps(d, 16, ((w0 * c2) + (w1 * c3))); + + _mm_store_ps(d, 48, ((w0 * c3) - (w1 * c2))); + /* + y[2] = c2 * r[6] + c3 * r[2]; + y[6] = c3 * r[6] - c2 * r[2]; + */ + + w0 = new Vector4(1.175876f); + w1 = new Vector4(0.785695f); + c3 = ((w0 * t4) + (w1 * t7)); + c0 = ((w0 * t7) - (w1 * t4)); + /* + c3 = t4 * r[3] + t7 * r[5]; + c0 = t7 * r[3] - t4 * r[5]; + */ + + w0 = new Vector4(1.387040f); + w1 = new Vector4(0.275899f); + c2 = ((w0 * t5) + (w1 * t6)); + c1 = ((w0 * t6) - (w1 * t5)); + /* + c2 = t5 * r[1] + t6 * r[7]; + c1 = t6 * r[1] - t5 * r[7]; + */ + + _mm_store_ps(d, 24, (c0 - c2)); + + _mm_store_ps(d, 40, (c3 - c1)); + //y[5] = c3 - c1; y[3] = c0 - c2; + + Vector4 invsqrt2 = new Vector4(0.707107f); + c0 = ((c0 + c2) * invsqrt2); + c3 = ((c3 + c1) * invsqrt2); + //c0 = (c0 + c2) * invsqrt2; + //c3 = (c3 + c1) * invsqrt2; + + _mm_store_ps(d, 8, (c0 + c3)); + + _mm_store_ps(d, 56, (c0 - c3)); + //y[1] = c0 + c3; y[7] = c0 - c3; + + /*for(i = 0;i < 8;i++) + { + y[i] *= invsqrt2h; + }*/ + } + + public static void fDCT8x8_llm_sse(Span s, Span d, Span temp) + { + ReferenceImplementations.Transpose8x8(s, temp); + + fDCT2D8x4_32f(temp, d); + + fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); + + ReferenceImplementations.Transpose8x8(d, temp); + + fDCT2D8x4_32f(temp, d); + + fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); + + Vector4 c = new Vector4(0.1250f); + + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//0 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//1 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//2 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//3 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//4 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//5 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//6 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//7 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//8 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//9 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//10 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//11 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//12 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//13 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//14 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//15 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 _mm_load_ps(Span src, int offset) + { + src = src.Slice(offset); + return new Vector4(src[0], src[1], src[2], src[3]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void _mm_store_ps(Span dest, int offset, Vector4 src) + { + dest = dest.Slice(offset); + dest[0] = src.X; + dest[1] = src.Y; + dest[2] = src.Z; + dest[3] = src.W; + } + + // Accurate variants of constants from: + // https://github.com/mozilla/mozjpeg/blob/master/simd/jfdctint-altivec.c + + private static readonly Vector4 _1_175876 = new Vector4(1.175875602f); + + private static readonly Vector4 _1_961571 = new Vector4(-1.961570560f); + + private static readonly Vector4 _0_390181 = new Vector4(-0.390180644f); + + private static readonly Vector4 _0_899976 = new Vector4(-0.899976223f); + + private static readonly Vector4 _2_562915 = new Vector4(-2.562915447f); + + private static readonly Vector4 _0_298631 = new Vector4(0.298631336f); + + private static readonly Vector4 _2_053120 = new Vector4(2.053119869f); + + private static readonly Vector4 _3_072711 = new Vector4(3.072711026f); + + private static readonly Vector4 _1_501321 = new Vector4(1.501321110f); + + private static readonly Vector4 _0_541196 = new Vector4(0.541196100f); + + private static readonly Vector4 _1_847759 = new Vector4(-1.847759065f); + + private static readonly Vector4 _0_765367 = new Vector4(0.765366865f); + + /// + /// Original: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 + /// Does a part of the IDCT job on the given parts of the blocks + /// + /// + /// + internal static void iDCT2D8x4_32f(Span y, Span x) + { + /* + float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; + for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } + */ + /* + 0: 1.414214 + 1: 1.387040 + 2: 1.306563 + 3: + 4: 1.000000 + 5: 0.785695 + 6: + 7: 0.275899 + */ + + Vector4 my1 = _mm_load_ps(y, 8); + Vector4 my7 = _mm_load_ps(y, 56); + Vector4 mz0 = my1 + my7; + + Vector4 my3 = _mm_load_ps(y, 24); + Vector4 mz2 = my3 + my7; + Vector4 my5 = _mm_load_ps(y, 40); + Vector4 mz1 = my3 + my5; + Vector4 mz3 = my1 + my5; + + Vector4 mz4 = ((mz0 + mz1) * _1_175876); + //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; + //z4 = (z0 + z1) * r[3]; + + mz2 = mz2 * _1_961571 + mz4; + mz3 = mz3 * _0_390181 + mz4; + mz0 = mz0 * _0_899976; + mz1 = mz1 * _2_562915; + + /* + -0.899976 + -2.562915 + -1.961571 + -0.390181 + z0 = z0 * (-r[3] + r[7]); + z1 = z1 * (-r[3] - r[1]); + z2 = z2 * (-r[3] - r[5]) + z4; + z3 = z3 * (-r[3] + r[5]) + z4;*/ + + Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; + Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; + Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; + Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; + + /* + 0.298631 + 2.053120 + 3.072711 + 1.501321 + b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; + b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; + b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; + b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; + */ + + Vector4 my2 = _mm_load_ps(y, 16); + Vector4 my6 = _mm_load_ps(y, 48); + mz4 = (my2 + my6) * _0_541196; + Vector4 my0 = _mm_load_ps(y, 0); + Vector4 my4 = _mm_load_ps(y, 32); + mz0 = my0 + my4; + mz1 = my0 - my4; + + mz2 = mz4 + my6 * _1_847759; + mz3 = mz4 + my2 * _0_765367; + + my0 = mz0 + mz3; + my3 = mz0 - mz3; + my1 = mz1 + mz2; + my2 = mz1 - mz2; + /* + 1.847759 + 0.765367 + z4 = (y[2] + y[6]) * r[6]; + z0 = y[0] + y[4]; z1 = y[0] - y[4]; + z2 = z4 - y[6] * (r[2] + r[6]); + z3 = z4 + y[2] * (r[2] - r[6]); + a0 = z0 + z3; a3 = z0 - z3; + a1 = z1 + z2; a2 = z1 - z2; + */ + + _mm_store_ps(x, 0, my0 + mb0); + + _mm_store_ps(x, 56, my0 - mb0); + + _mm_store_ps(x, 8, my1 + mb1); + + _mm_store_ps(x, 48, my1 - mb1); + + _mm_store_ps(x, 16, my2 + mb2); + + _mm_store_ps(x, 40, my2 - mb2); + + _mm_store_ps(x, 24, my3 + mb3); + + _mm_store_ps(x, 32, my3 - mb3); + /* + x[0] = a0 + b0; x[7] = a0 - b0; + x[1] = a1 + b1; x[6] = a1 - b1; + x[2] = a2 + b2; x[5] = a2 - b2; + x[3] = a3 + b3; x[4] = a3 - b3; + for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } + */ + } + + internal static void fDCT1Dllm_32f(Span x, Span y) + { + float t0, t1, t2, t3, t4, t5, t6, t7; + float c0, c1, c2, c3; + float[] r = new float[8]; + + //for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } + r[0] = 1.414214f; + r[1] = 1.387040f; + r[2] = 1.306563f; + r[3] = 1.175876f; + r[4] = 1.000000f; + r[5] = 0.785695f; + r[6] = 0.541196f; + r[7] = 0.275899f; + + const float invsqrt2 = 0.707107f; //(float)(1.0f / M_SQRT2); + //const float invsqrt2h = 0.353554f; //invsqrt2*0.5f; + + c1 = x[0]; + c2 = x[7]; + t0 = c1 + c2; + t7 = c1 - c2; + c1 = x[1]; + c2 = x[6]; + t1 = c1 + c2; + t6 = c1 - c2; + c1 = x[2]; + c2 = x[5]; + t2 = c1 + c2; + t5 = c1 - c2; + c1 = x[3]; + c2 = x[4]; + t3 = c1 + c2; + t4 = c1 - c2; + + c0 = t0 + t3; + c3 = t0 - t3; + c1 = t1 + t2; + c2 = t1 - t2; + + y[0] = c0 + c1; + y[4] = c0 - c1; + y[2] = c2 * r[6] + c3 * r[2]; + y[6] = c3 * r[6] - c2 * r[2]; + + c3 = t4 * r[3] + t7 * r[5]; + c0 = t7 * r[3] - t4 * r[5]; + c2 = t5 * r[1] + t6 * r[7]; + c1 = t6 * r[1] - t5 * r[7]; + + y[5] = c3 - c1; + y[3] = c0 - c2; + c0 = (c0 + c2) * invsqrt2; + c3 = (c3 + c1) * invsqrt2; + y[1] = c0 + c3; + y[7] = c0 - c3; + } + + internal static void fDCT2D_llm( + Span s, + Span d, + Span temp, + bool downscaleBy8 = false, + bool subtract128FromSource = false) + { + Span sWorker = subtract128FromSource ? s.AddScalarToAllValues(-128f) : s; + + for (int j = 0; j < 8; j++) + { + fDCT1Dllm_32f(sWorker.Slice(j * 8), temp.Slice(j * 8)); + } + + ReferenceImplementations.Transpose8x8(temp, d); + + for (int j = 0; j < 8; j++) + { + fDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); + } + + ReferenceImplementations.Transpose8x8(temp, d); + + if (downscaleBy8) + { + for (int j = 0; j < 64; j++) + { + d[j] *= 0.125f; + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs new file mode 100644 index 0000000000..9afc4b0b3b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs @@ -0,0 +1,367 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + internal static partial class ReferenceImplementations + { + /// + /// TODO: produces really bad results for bigger values! + /// + /// Contains the "original" golang based DCT/IDCT implementations as reference implementations. + /// 1. ===== Forward DCT ===== + /// **** The original golang source claims: + /// It is based on the code in jfdctint.c from the Independent JPEG Group, + /// found at http://www.ijg.org/files/jpegsrc.v8c.tar.gz. + /// + /// **** Could be found here as well: + /// https://github.com/mozilla/mozjpeg/blob/master/jfdctint.c + /// + /// 2. ===== Inverse DCT ===== + /// + /// The golang source claims: + /// http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_IEC_13818-4_2004_Conformance_Testing/Video/verifier/mpeg2decode_960109.tar.gz + /// The referenced MPEG2 code claims: + /// /**********************************************************/ + /// /* inverse two dimensional DCT, Chen-Wang algorithm */ + /// /* (cf. IEEE ASSP-32, pp. 803-816, Aug. 1984) */ + /// /* 32-bit integer arithmetic (8 bit coefficients) */ + /// /* 11 mults, 29 adds per DCT */ + /// /* sE, 18.8.91 */ + /// /**********************************************************/ + /// /* coefficients extended to 12 bit for IEEE1180-1990 */ + /// /* compliance sE, 2.1.94 */ + /// /**********************************************************/ + /// + /// **** The code looks pretty similar to the standard libjpeg IDCT, but without quantization: + /// https://github.com/mozilla/mozjpeg/blob/master/jidctint.c + /// + public static class StandardIntegerDCT + { + private const int fix_0_298631336 = 2446; + private const int fix_0_390180644 = 3196; + private const int fix_0_541196100 = 4433; + private const int fix_0_765366865 = 6270; + private const int fix_0_899976223 = 7373; + private const int fix_1_175875602 = 9633; + private const int fix_1_501321110 = 12299; + private const int fix_1_847759065 = 15137; + private const int fix_1_961570560 = 16069; + private const int fix_2_053119869 = 16819; + private const int fix_2_562915447 = 20995; + private const int fix_3_072711026 = 25172; + + /// + /// The number of bits + /// + private const int Bits = 13; + + /// + /// The number of bits to shift by on the first pass. + /// + private const int Pass1Bits = 2; + + /// + /// The value to shift by + /// + private const int CenterJSample = 128; + + public static Block8x8 Subtract128_TransformFDCT_Upscale8(ref Block8x8 block) + { + int[] temp = new int[Block8x8.Size]; + block.CopyTo(temp); + Subtract128_TransformFDCT_Upscale8_Inplace(temp); + var result = default(Block8x8); + result.LoadFrom(temp); + return result; + } + + // [Obsolete("Looks like this method produces really bad results for bigger values!")] + public static Block8x8 TransformIDCT(ref Block8x8 block) + { + int[] temp = new int[Block8x8.Size]; + block.CopyTo(temp); + TransformIDCTInplace(temp); + var result = default(Block8x8); + result.LoadFrom(temp); + return result; + } + + /// + /// Performs a forward DCT on an 8x8 block of coefficients, including a level shift. + /// Leave results scaled up by an overall factor of 8. + /// + /// The block of coefficients. + public static void Subtract128_TransformFDCT_Upscale8_Inplace(Span block) + { + // Pass 1: process rows. + for (int y = 0; y < 8; y++) + { + int y8 = y * 8; + + int x0 = block[y8]; + int x1 = block[y8 + 1]; + int x2 = block[y8 + 2]; + int x3 = block[y8 + 3]; + int x4 = block[y8 + 4]; + int x5 = block[y8 + 5]; + int x6 = block[y8 + 6]; + int x7 = block[y8 + 7]; + + int tmp0 = x0 + x7; + int tmp1 = x1 + x6; + int tmp2 = x2 + x5; + int tmp3 = x3 + x4; + + int tmp10 = tmp0 + tmp3; + int tmp12 = tmp0 - tmp3; + int tmp11 = tmp1 + tmp2; + int tmp13 = tmp1 - tmp2; + + tmp0 = x0 - x7; + tmp1 = x1 - x6; + tmp2 = x2 - x5; + tmp3 = x3 - x4; + + block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits; + block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits; + int z1 = (tmp12 + tmp13) * fix_0_541196100; + z1 += 1 << (Bits - Pass1Bits - 1); + block[y8 + 2] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits - Pass1Bits); + block[y8 + 6] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits - Pass1Bits); + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = (tmp12 + tmp13) * fix_1_175875602; + z1 += 1 << (Bits - Pass1Bits - 1); + tmp0 = tmp0 * fix_1_501321110; + tmp1 = tmp1 * fix_3_072711026; + tmp2 = tmp2 * fix_2_053119869; + tmp3 = tmp3 * fix_0_298631336; + tmp10 = tmp10 * -fix_0_899976223; + tmp11 = tmp11 * -fix_2_562915447; + tmp12 = tmp12 * -fix_0_390180644; + tmp13 = tmp13 * -fix_1_961570560; + + tmp12 += z1; + tmp13 += z1; + block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits); + block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits); + block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits); + block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits); + } + + // Pass 2: process columns. + // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8. + for (int x = 0; x < 8; x++) + { + int tmp0 = block[x] + block[56 + x]; + int tmp1 = block[8 + x] + block[48 + x]; + int tmp2 = block[16 + x] + block[40 + x]; + int tmp3 = block[24 + x] + block[32 + x]; + + int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1)); + int tmp12 = tmp0 - tmp3; + int tmp11 = tmp1 + tmp2; + int tmp13 = tmp1 - tmp2; + + tmp0 = block[x] - block[56 + x]; + tmp1 = block[8 + x] - block[48 + x]; + tmp2 = block[16 + x] - block[40 + x]; + tmp3 = block[24 + x] - block[32 + x]; + + block[x] = (tmp10 + tmp11) >> Pass1Bits; + block[32 + x] = (tmp10 - tmp11) >> Pass1Bits; + + int z1 = (tmp12 + tmp13) * fix_0_541196100; + z1 += 1 << (Bits + Pass1Bits - 1); + block[16 + x] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits + Pass1Bits); + block[48 + x] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits + Pass1Bits); + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = (tmp12 + tmp13) * fix_1_175875602; + z1 += 1 << (Bits + Pass1Bits - 1); + tmp0 = tmp0 * fix_1_501321110; + tmp1 = tmp1 * fix_3_072711026; + tmp2 = tmp2 * fix_2_053119869; + tmp3 = tmp3 * fix_0_298631336; + tmp10 = tmp10 * -fix_0_899976223; + tmp11 = tmp11 * -fix_2_562915447; + tmp12 = tmp12 * -fix_0_390180644; + tmp13 = tmp13 * -fix_1_961570560; + + tmp12 += z1; + tmp13 += z1; + block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits); + block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits); + block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits); + block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits); + } + + } + private const int w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16) + private const int w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16) + private const int w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16) + private const int w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16) + private const int w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16) + private const int w7 = 565; // 2048*sqrt(2)*cos(7*pi/16) + + private const int w1pw7 = w1 + w7; + private const int w1mw7 = w1 - w7; + private const int w2pw6 = w2 + w6; + private const int w2mw6 = w2 - w6; + private const int w3pw5 = w3 + w5; + private const int w3mw5 = w3 - w5; + + private const int r2 = 181; // 256/sqrt(2) + + /// + /// Performs a 2-D Inverse Discrete Cosine Transformation. + /// + /// The input coefficients should already have been multiplied by the + /// appropriate quantization table. We use fixed-point computation, with the + /// number of bits for the fractional component varying over the intermediate + /// stages. + /// + /// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the + /// discrete W transform and for the discrete Fourier transform", IEEE Trans. on + /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. + /// + /// The source block of coefficients + // [Obsolete("Looks like this method produces really bad results for bigger values!")] + public static void TransformIDCTInplace(Span src) + { + // Horizontal 1-D IDCT. + for (int y = 0; y < 8; y++) + { + int y8 = y * 8; + + // If all the AC components are zero, then the IDCT is trivial. + if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 && + src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0) + { + int dc = src[y8 + 0] << 3; + src[y8 + 0] = dc; + src[y8 + 1] = dc; + src[y8 + 2] = dc; + src[y8 + 3] = dc; + src[y8 + 4] = dc; + src[y8 + 5] = dc; + src[y8 + 6] = dc; + src[y8 + 7] = dc; + continue; + } + + // Prescale. + int x0 = (src[y8 + 0] << 11) + 128; + int x1 = src[y8 + 4] << 11; + int x2 = src[y8 + 6]; + int x3 = src[y8 + 2]; + int x4 = src[y8 + 1]; + int x5 = src[y8 + 7]; + int x6 = src[y8 + 5]; + int x7 = src[y8 + 3]; + + // Stage 1. + int x8 = w7 * (x4 + x5); + x4 = x8 + (w1mw7 * x4); + x5 = x8 - (w1pw7 * x5); + x8 = w3 * (x6 + x7); + x6 = x8 - (w3mw5 * x6); + x7 = x8 - (w3pw5 * x7); + + // Stage 2. + x8 = x0 + x1; + x0 -= x1; + x1 = w6 * (x3 + x2); + x2 = x1 - (w2pw6 * x2); + x3 = x1 + (w2mw6 * x3); + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + + // Stage 3. + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = ((r2 * (x4 + x5)) + 128) >> 8; + x4 = ((r2 * (x4 - x5)) + 128) >> 8; + + // Stage 4. + src[y8 + 0] = (x7 + x1) >> 8; + src[y8 + 1] = (x3 + x2) >> 8; + src[y8 + 2] = (x0 + x4) >> 8; + src[y8 + 3] = (x8 + x6) >> 8; + src[y8 + 4] = (x8 - x6) >> 8; + src[y8 + 5] = (x0 - x4) >> 8; + src[y8 + 6] = (x3 - x2) >> 8; + src[y8 + 7] = (x7 - x1) >> 8; + } + + // Vertical 1-D IDCT. + for (int x = 0; x < 8; x++) + { + // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial. + // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so + // we do not bother to check for the all-zero case. + + // Prescale. + int y0 = (src[x] << 8) + 8192; + int y1 = src[32 + x] << 8; + int y2 = src[48 + x]; + int y3 = src[16 + x]; + int y4 = src[8 + x]; + int y5 = src[56 + x]; + int y6 = src[40 + x]; + int y7 = src[24 + x]; + + // Stage 1. + int y8 = (w7 * (y4 + y5)) + 4; + y4 = (y8 + (w1mw7 * y4)) >> 3; + y5 = (y8 - (w1pw7 * y5)) >> 3; + y8 = (w3 * (y6 + y7)) + 4; + y6 = (y8 - (w3mw5 * y6)) >> 3; + y7 = (y8 - (w3pw5 * y7)) >> 3; + + // Stage 2. + y8 = y0 + y1; + y0 -= y1; + y1 = (w6 * (y3 + y2)) + 4; + y2 = (y1 - (w2pw6 * y2)) >> 3; + y3 = (y1 + (w2mw6 * y3)) >> 3; + y1 = y4 + y6; + y4 -= y6; + y6 = y5 + y7; + y5 -= y7; + + // Stage 3. + y7 = y8 + y3; + y8 -= y3; + y3 = y0 + y2; + y0 -= y2; + y2 = ((r2 * (y4 + y5)) + 128) >> 8; + y4 = ((r2 * (y4 - y5)) + 128) >> 8; + + // Stage 4. + src[x] = (y7 + y1) >> 14; + src[8 + x] = (y3 + y2) >> 14; + src[16 + x] = (y0 + y4) >> 14; + src[24 + x] = (y8 + y6) >> 14; + src[32 + x] = (y8 - y6) >> 14; + src[40 + x] = (y0 - y4) >> 14; + src[48 + x] = (y3 - y2) >> 14; + src[56 + x] = (y7 - y1) >> 14; + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs new file mode 100644 index 0000000000..92ead8164f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs @@ -0,0 +1,150 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + + +// ReSharper disable InconsistentNaming + + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + /// + /// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests + /// Floating point DCT code Ported from https://github.com/norishigefukushima/dct_simd + /// + internal static partial class ReferenceImplementations + { + public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) + { + float* b = (float*)blockPtr; + float* qtp = (float*)qtPtr; + for (int qtIndex = 0; qtIndex < Block8x8F.Size; qtIndex++) + { + int i = unzigPtr[qtIndex]; + float* unzigPos = b + i; + + float val = *unzigPos; + val *= qtp[qtIndex]; + *unzigPos = val; + } + } + + /// + /// Transpose 8x8 block stored linearly in a (inplace) + /// + /// + internal static void Transpose8x8(Span data) + { + for (int i = 1; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < i; j++) + { + float tmp = data[i8 + j]; + data[i8 + j] = data[j * 8 + i]; + data[j * 8 + i] = tmp; + } + } + } + + /// + /// Transpose 8x8 block stored linearly in a + /// + internal static void Transpose8x8(Span src, Span dest) + { + for (int i = 0; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < 8; j++) + { + dest[j * 8 + i] = src[i8 + j]; + } + } + } + + /// + /// Copies color values from block to the destination image buffer. + /// + /// + /// + /// + internal static unsafe void CopyColorsTo(ref Block8x8F block, Span buffer, int stride) + { + fixed (Block8x8F* p = &block) + { + float* b = (float*)p; + + for (int y = 0; y < 8; y++) + { + int y8 = y * 8; + int yStride = y * stride; + + for (int x = 0; x < 8; x++) + { + float c = b[y8 + x]; + + if (c < -128) + { + c = 0; + } + else if (c > 127) + { + c = 255; + } + else + { + c += 128; + } + + buffer[yStride + x] = (byte)c; + } + } + } + } + + /// + /// Reference implementation to test . + /// Rounding is done used an integer-based algorithm defined in . + /// + /// The input block + /// The destination block of integers + /// The quantization table + /// Pointer to + public static unsafe void QuantizeRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr) + { + float* s = (float*)src; + float* q = (float*)qt; + + for (int zig = 0; zig < Block8x8F.Size; zig++) + { + int a = (int)s[unzigPtr[zig]]; + int b = (int)q[zig]; + + int val = RationalRound(a, b); + dest[zig] = val; + } + } + + /// + /// Rounds a rational number defined as dividend/divisor into an integer + /// + /// The dividend + /// The divisior + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RationalRound(int dividend, int divisor) + { + if (dividend >= 0) + { + return (dividend + (divisor >> 1)) / divisor; + } + + return -((-dividend + (divisor >> 1)) / divisor); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs new file mode 100644 index 0000000000..988b9e4781 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs @@ -0,0 +1,121 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Span Extensions + /// + internal static class SpanExtensions + { + /// + /// Save to a Vector4 + /// + /// The data + /// The vector to save to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SaveTo(this Span data, ref Vector4 v) + { + v.X = data[0]; + v.Y = data[1]; + v.Z = data[2]; + v.W = data[3]; + } + + /// + /// Save to a Vector4 + /// + /// The data + /// The vector to save to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SaveTo(this Span data, ref Vector4 v) + { + v.X = data[0]; + v.Y = data[1]; + v.Z = data[2]; + v.W = data[3]; + } + + /// + /// Load from Vector4 + /// + /// The data + /// The vector to load from + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LoadFrom(this Span data, ref Vector4 v) + { + data[0] = v.X; + data[1] = v.Y; + data[2] = v.Z; + data[3] = v.W; + } + + /// + /// Load from Vector4 + /// + /// The data + /// The vector to load from + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LoadFrom(this Span data, ref Vector4 v) + { + data[0] = (int)v.X; + data[1] = (int)v.Y; + data[2] = (int)v.Z; + data[3] = (int)v.W; + } + + /// + /// Converts all int values of src to float + /// + /// Source + /// A new with float values + public static float[] ConvertAllToFloat(this int[] src) + { + float[] result = new float[src.Length]; + for (int i = 0; i < src.Length; i++) + { + result[i] = (float)src[i]; + } + + return result; + } + + /// + /// Add a scalar to all values of src + /// + /// The source + /// The scalar value to add + /// A new instance of + public static Span AddScalarToAllValues(this Span src, float scalar) + { + float[] result = new float[src.Length]; + for (int i = 0; i < src.Length; i++) + { + result[i] = src[i] + scalar; + } + + return result; + } + + /// + /// Add a scalar to all values of src + /// + /// The source + /// The scalar value to add + /// A new instance of + public static Span AddScalarToAllValues(this Span src, int scalar) + { + int[] result = new int[src.Length]; + for (int i = 0; i < src.Length; i++) + { + result[i] = src[i] + scalar; + } + + return result; + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs new file mode 100644 index 0000000000..d0f7df12ce --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; + +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + internal static class VerifyJpeg + { + internal static void VerifySize(IJpegComponent component, int expectedBlocksX, int expectedBlocksY) + { + Assert.Equal(new Size(expectedBlocksX, expectedBlocksY), component.SizeInBlocks); + } + + internal static void VerifyComponent( + IJpegComponent component, + Size expectedSizeInBlocks, + Size expectedSamplingFactors, + Size expectedSubsamplingDivisors) + { + Assert.Equal(expectedSizeInBlocks, component.SizeInBlocks); + Assert.Equal(expectedSamplingFactors, component.SamplingFactors); + Assert.Equal(expectedSubsamplingDivisors, component.SubSamplingDivisors); + } + + internal static void VerifyComponentSizes3( + IEnumerable components, + int xBc0, + int yBc0, + int xBc1, + int yBc1, + int xBc2, + int yBc2) + { + IJpegComponent[] c = components.ToArray(); + Assert.Equal(3, components.Count()); + + VerifySize(c[0], xBc0, yBc0); + VerifySize(c[1], xBc1, yBc1); + VerifySize(c[2], xBc2, yBc2); + } + + internal static void SaveSpectralImage( + TestImageProvider provider, + LibJpegTools.SpectralData data, + ITestOutputHelper output = null) + where TPixel : struct, IPixel + { + foreach (LibJpegTools.ComponentData comp in data.Components) + { + output?.WriteLine("Min: " + comp.MinVal); + output?.WriteLine("Max: " + comp.MaxVal); + + using (Image image = comp.CreateGrayScaleImage()) + { + string details = $"C{comp.Index}"; + image.DebugSave(provider, details, appendPixelTypeToFileName: false); + } + } + + Image fullImage = data.TryCreateRGBSpectralImage(); + + if (fullImage != null) + { + fullImage.DebugSave(provider, "FULL", appendPixelTypeToFileName: false); + fullImage.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs deleted file mode 100644 index b911e11846..0000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using ImageSharp.Formats.Jpg; - using SixLabors.Primitives; - using Xunit; - using Xunit.Abstractions; - - public class YCbCrImageTests - { - public YCbCrImageTests(ITestOutputHelper output) - { - this.Output = output; - } - - private ITestOutputHelper Output { get; } - - [Theory] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410, 4, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411, 4, 1)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420, 2, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422, 2, 1)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440, 1, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444, 1, 1)] - internal void CalculateChrominanceSize( - YCbCrImage.YCbCrSubsampleRatio ratioValue, - int expectedDivX, - int expectedDivY) - { - YCbCrImage.YCbCrSubsampleRatio ratio = (YCbCrImage.YCbCrSubsampleRatio)ratioValue; - - //this.Output.WriteLine($"RATIO: {ratio}"); - Size size = YCbCrImage.CalculateChrominanceSize(400, 400, ratio); - //this.Output.WriteLine($"Ch Size: {size}"); - - Assert.Equal(new Size(400 / expectedDivX, 400 / expectedDivY), size); - } - - [Theory] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410, 4)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411, 4)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440, 1)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444, 1)] - internal void Create(YCbCrImage.YCbCrSubsampleRatio ratioValue, int expectedCStrideDiv) - { - YCbCrImage.YCbCrSubsampleRatio ratio = (YCbCrImage.YCbCrSubsampleRatio)ratioValue; - - this.Output.WriteLine($"RATIO: {ratio}"); - - YCbCrImage img = new YCbCrImage(400, 400, ratio); - - //this.PrintChannel("Y", img.YChannel); - //this.PrintChannel("Cb", img.CbChannel); - //this.PrintChannel("Cr", img.CrChannel); - - Assert.Equal(400, img.YChannel.Width); - Assert.Equal(img.CbChannel.Width, 400 / expectedCStrideDiv); - Assert.Equal(img.CrChannel.Width, 400 / expectedCStrideDiv); - } - - private void PrintChannel(string name, JpegPixelArea channel) - { - this.Output.WriteLine($"{name}: Stride={channel.Stride}"); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ycck.jpg b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/baseline/ycck - Copy.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ycck.jpg rename to tests/ImageSharp.Tests/Formats/Jpg/pdfjs/baseline/ycck - Copy.jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm new file mode 100644 index 0000000000..40af957fc4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm @@ -0,0 +1,89 @@ + + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm new file mode 100644 index 0000000000..72a5e448b3 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm @@ -0,0 +1,63 @@ + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js new file mode 100644 index 0000000000..6ebf71a697 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js @@ -0,0 +1,1205 @@ +/* Copyright 2014 Mozilla Foundation + * + * 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. + */ +/* eslint-disable no-multi-spaces */ + +// import { error, warn } from '../shared/util'; + +/** + * This code was forked from https://github.com/notmasteryet/jpgjs. + * The original version was created by GitHub user notmasteryet. + * + * - The JPEG specification can be found in the ITU CCITT Recommendation T.81 + * (www.w3.org/Graphics/JPEG/itu-t81.pdf) + * - The JFIF specification can be found in the JPEG File Interchange Format + * (www.w3.org/Graphics/JPEG/jfif3.pdf) + * - The Adobe Application-Specific JPEG markers in the + * Supporting the DCT Filters in PostScript Level 2, Technical Note #5116 + * (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf) + */ + +var error = function(val){ + console.log(val); +} + +var warn = function(val){ + console.log(val); +} + +var JpegImage = (function JpegImageClosure() { + var dctZigZag = new Uint8Array([ + 0, + 1, 8, + 16, 9, 2, + 3, 10, 17, 24, + 32, 25, 18, 11, 4, + 5, 12, 19, 26, 33, 40, + 48, 41, 34, 27, 20, 13, 6, + 7, 14, 21, 28, 35, 42, 49, 56, + 57, 50, 43, 36, 29, 22, 15, + 23, 30, 37, 44, 51, 58, + 59, 52, 45, 38, 31, + 39, 46, 53, 60, + 61, 54, 47, + 55, 62, + 63 + ]); + + var dctCos1 = 4017; // cos(pi/16) + var dctSin1 = 799; // sin(pi/16) + var dctCos3 = 3406; // cos(3*pi/16) + var dctSin3 = 2276; // sin(3*pi/16) + var dctCos6 = 1567; // cos(6*pi/16) + var dctSin6 = 3784; // sin(6*pi/16) + var dctSqrt2 = 5793; // sqrt(2) + var dctSqrt1d2 = 2896; // sqrt(2) / 2 + + function JpegImage() { + this.decodeTransform = null; + this.colorTransform = -1; + } + + function buildHuffmanTable(codeLengths, values) { + console.log(codeLengths); + console.log(values); + + var k = 0, code = [], i, j, length = 16; + while (length > 0 && !codeLengths[length - 1]) { + length--; + } + + code.push({ children: [], index: 0, }); + var p = code[0], q; + for (i = 0; i < length; i++) { + for (j = 0; j < codeLengths[i]; j++) { + p = code.pop(); + p.children[p.index] = values[k]; + + while (p.index > 0) { + p = code.pop(); + } + p.index++; + code.push(p); + while (code.length <= i) { + code.push(q = { children: [], index: 0, }); + p.children[p.index] = q.children; + p = q; + } + k++; + } + if (i + 1 < length) { + // p here points to last code + code.push(q = { children: [], index: 0, }); + p.children[p.index] = q.children; + p = q; + } + } + console.log(code[0].children); + console.log(k); + return code[0].children; + } + + function getBlockBufferOffset(component, row, col) { + return 64 * ((component.blocksPerLine + 1) * row + col); + } + + function decodeScan(data, offset, frame, components, resetInterval, + spectralStart, spectralEnd, successivePrev, successive) { + var mcusPerLine = frame.mcusPerLine; + var progressive = frame.progressive; + var startOffset = offset, bitsData = 0, bitsCount = 0; + + function readBit() { + if (bitsCount > 0) { + bitsCount--; + return (bitsData >> bitsCount) & 1; + } + bitsData = data[offset++]; + if (bitsData === 0xFF) { + var nextByte = data[offset++]; + if (nextByte) { + error('JPEG error: unexpected marker ' + + ((bitsData << 8) | nextByte).toString(16)); + } + // unstuff 0 + } + bitsCount = 7; + return bitsData >>> 7; + } + + function decodeHuffman(tree) { + var node = tree; + while (true) { + node = node[readBit()]; + if (typeof node === 'number') { + return node; + } + if (typeof node !== 'object') { + error('JPEG error: invalid huffman sequence'); + } + } + } + + function receive(length) { + var n = 0; + while (length > 0) { + n = (n << 1) | readBit(); + length--; + } + return n; + } + + function receiveAndExtend(length) { + if (length === 1) { + return readBit() === 1 ? 1 : -1; + } + var n = receive(length); + if (n >= 1 << (length - 1)) { + return n; + } + return n + (-1 << length) + 1; + } + + function decodeBaseline(component, offset) { + var t = decodeHuffman(component.huffmanTableDC); + var diff = t === 0 ? 0 : receiveAndExtend(t); + component.blockData[offset] = (component.pred += diff); + // console.log("component"); + // console.log(component); + + if(offset === 0){ + console.log("component at 0"); + console.log(component.blockData[offset]) + } + + var k = 1; + while (k < 64) { + var rs = decodeHuffman(component.huffmanTableAC); + var s = rs & 15, r = rs >> 4; + if (s === 0) { + if (r < 15) { + break; + } + k += 16; + continue; + } + k += r; + var z = dctZigZag[k]; + component.blockData[offset + z] = receiveAndExtend(s); + k++; + } + } + + function decodeDCFirst(component, offset) { + var t = decodeHuffman(component.huffmanTableDC); + var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive); + component.blockData[offset] = (component.pred += diff); + } + + function decodeDCSuccessive(component, offset) { + component.blockData[offset] |= readBit() << successive; + } + + var eobrun = 0; + function decodeACFirst(component, offset) { + if (eobrun > 0) { + eobrun--; + return; + } + var k = spectralStart, e = spectralEnd; + while (k <= e) { + var rs = decodeHuffman(component.huffmanTableAC); + var s = rs & 15, r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r) - 1; + break; + } + k += 16; + continue; + } + k += r; + var z = dctZigZag[k]; + component.blockData[offset + z] = + receiveAndExtend(s) * (1 << successive); + k++; + } + } + + var successiveACState = 0, successiveACNextValue; + function decodeACSuccessive(component, offset) { + var k = spectralStart; + var e = spectralEnd; + var r = 0; + var s; + var rs; + while (k <= e) { + var z = dctZigZag[k]; + switch (successiveACState) { + case 0: // initial state + rs = decodeHuffman(component.huffmanTableAC); + s = rs & 15; + r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r); + successiveACState = 4; + } else { + r = 16; + successiveACState = 1; + } + } else { + if (s !== 1) { + error('JPEG error: invalid ACn encoding'); + } + successiveACNextValue = receiveAndExtend(s); + successiveACState = r ? 2 : 3; + } + continue; + case 1: // skipping r zero items + case 2: + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } else { + r--; + if (r === 0) { + successiveACState = successiveACState === 2 ? 3 : 0; + } + } + break; + case 3: // set value for a zero item + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } else { + component.blockData[offset + z] = + successiveACNextValue << successive; + successiveACState = 0; + } + break; + case 4: // eob + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } + break; + } + k++; + } + if (successiveACState === 4) { + eobrun--; + if (eobrun === 0) { + successiveACState = 0; + } + } + } + + function decodeMcu(component, decode, mcu, row, col) { + var mcuRow = (mcu / mcusPerLine) | 0; + var mcuCol = mcu % mcusPerLine; + var blockRow = mcuRow * component.v + row; + var blockCol = mcuCol * component.h + col; + var offset = getBlockBufferOffset(component, blockRow, blockCol); + + // console.log("MCU Offset: " + offset); + decode(component, offset); + } + + function decodeBlock(component, decode, mcu) { + var blockRow = (mcu / component.blocksPerLine) | 0; + var blockCol = mcu % component.blocksPerLine; + var offset = getBlockBufferOffset(component, blockRow, blockCol); + decode(component, offset); + } + + var componentsLength = components.length; + var component, i, j, k, n; + var decodeFn; + if (progressive) { + if (spectralStart === 0) { + decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive; + console.log(successivePrev === 0 ? "decodeDCFirst" : "decodeDCSuccessive"); + } else { + decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive; + console.log(successivePrev === 0 ? "decodeACFirst" : "decodeACSuccessive"); + } + } else { + decodeFn = decodeBaseline; + } + + var mcu = 0, fileMarker; + var mcuExpected; + if (componentsLength === 1) { + mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn; + } else { + mcuExpected = mcusPerLine * frame.mcusPerColumn; + } + + console.log("mcuExpected = "+ mcuExpected); + + var h, v; + while (mcu < mcuExpected) { + // reset interval stuff + var mcuToRead = resetInterval ? + Math.min(mcuExpected - mcu, resetInterval) : mcuExpected; + for (i = 0; i < componentsLength; i++) { + components[i].pred = 0; + } + eobrun = 0; + + if (componentsLength === 1) { + component = components[0]; + + for (n = 0; n < mcuToRead; n++) { + decodeBlock(component, decodeFn, mcu); + mcu++; + } + } else { + for (n = 0; n < mcuToRead; n++) { + for (i = 0; i < componentsLength; i++) { + component = components[i]; + h = component.h; + v = component.v; + for (j = 0; j < v; j++) { + for (k = 0; k < h; k++) { + decodeMcu(component, decodeFn, mcu, j, k); + } + } + } + mcu++; + } + } + + // find marker + bitsCount = 0; + fileMarker = findNextFileMarker(data, offset); + // Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past + // those to attempt to find a valid marker (fixes issue4090.pdf). + if (fileMarker && fileMarker.invalid) { + warn('decodeScan - unexpected MCU data, next marker is: ' + + fileMarker.invalid); + offset = fileMarker.offset; + } + var marker = fileMarker && fileMarker.marker; + if (!marker || marker <= 0xFF00) { + error('JPEG error: marker was not found'); + } + + if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx + offset += 2; + } else { + break; + } + } + + fileMarker = findNextFileMarker(data, offset); + // Some images include more Scan blocks than expected, skip past those and + // attempt to find the next valid marker (fixes issue8182.pdf). + if (fileMarker && fileMarker.invalid) { + warn('decodeScan - unexpected Scan data, next marker is: ' + + fileMarker.invalid); + offset = fileMarker.offset; + } + + return offset - startOffset; + } + + // A port of poppler's IDCT method which in turn is taken from: + // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, + // 'Practical Fast 1-D DCT Algorithms with 11 Multiplications', + // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, + // 988-991. + function quantizeAndInverse(component, blockBufferOffset, p) { + var qt = component.quantizationTable, blockData = component.blockData; + var v0, v1, v2, v3, v4, v5, v6, v7; + var p0, p1, p2, p3, p4, p5, p6, p7; + var t; + + if (!qt) { + error('JPEG error: missing required Quantization Table.'); + } + + // inverse DCT on rows + for (var row = 0; row < 64; row += 8) { + // gather block data + p0 = blockData[blockBufferOffset + row]; + p1 = blockData[blockBufferOffset + row + 1]; + p2 = blockData[blockBufferOffset + row + 2]; + p3 = blockData[blockBufferOffset + row + 3]; + p4 = blockData[blockBufferOffset + row + 4]; + p5 = blockData[blockBufferOffset + row + 5]; + p6 = blockData[blockBufferOffset + row + 6]; + p7 = blockData[blockBufferOffset + row + 7]; + + // dequant p0 + p0 *= qt[row]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) { + t = (dctSqrt2 * p0 + 512) >> 10; + p[row] = t; + p[row + 1] = t; + p[row + 2] = t; + p[row + 3] = t; + p[row + 4] = t; + p[row + 5] = t; + p[row + 6] = t; + p[row + 7] = t; + continue; + } + // dequant p1 ... p7 + p1 *= qt[row + 1]; + p2 *= qt[row + 2]; + p3 *= qt[row + 3]; + p4 *= qt[row + 4]; + p5 *= qt[row + 5]; + p6 *= qt[row + 6]; + p7 *= qt[row + 7]; + + // stage 4 + v0 = (dctSqrt2 * p0 + 128) >> 8; + v1 = (dctSqrt2 * p4 + 128) >> 8; + v2 = p2; + v3 = p6; + v4 = (dctSqrt1d2 * (p1 - p7) + 128) >> 8; + v7 = (dctSqrt1d2 * (p1 + p7) + 128) >> 8; + v5 = p3 << 4; + v6 = p5 << 4; + + // stage 3 + v0 = (v0 + v1 + 1) >> 1; + v1 = v0 - v1; + t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p[row] = v0 + v7; + p[row + 7] = v0 - v7; + p[row + 1] = v1 + v6; + p[row + 6] = v1 - v6; + p[row + 2] = v2 + v5; + p[row + 5] = v2 - v5; + p[row + 3] = v3 + v4; + p[row + 4] = v3 - v4; + } + + // inverse DCT on columns + for (var col = 0; col < 8; ++col) { + p0 = p[col]; + p1 = p[col + 8]; + p2 = p[col + 16]; + p3 = p[col + 24]; + p4 = p[col + 32]; + p5 = p[col + 40]; + p6 = p[col + 48]; + p7 = p[col + 56]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) { + t = (dctSqrt2 * p0 + 8192) >> 14; + // convert to 8 bit + t = (t < -2040) ? 0 : (t >= 2024) ? 255 : (t + 2056) >> 4; + blockData[blockBufferOffset + col] = t; + blockData[blockBufferOffset + col + 8] = t; + blockData[blockBufferOffset + col + 16] = t; + blockData[blockBufferOffset + col + 24] = t; + blockData[blockBufferOffset + col + 32] = t; + blockData[blockBufferOffset + col + 40] = t; + blockData[blockBufferOffset + col + 48] = t; + blockData[blockBufferOffset + col + 56] = t; + continue; + } + + // stage 4 + v0 = (dctSqrt2 * p0 + 2048) >> 12; + v1 = (dctSqrt2 * p4 + 2048) >> 12; + v2 = p2; + v3 = p6; + v4 = (dctSqrt1d2 * (p1 - p7) + 2048) >> 12; + v7 = (dctSqrt1d2 * (p1 + p7) + 2048) >> 12; + v5 = p3; + v6 = p5; + + // stage 3 + // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when + // converting to UInt8 range later. + v0 = ((v0 + v1 + 1) >> 1) + 4112; + v1 = v0 - v1; + t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p0 = v0 + v7; + p7 = v0 - v7; + p1 = v1 + v6; + p6 = v1 - v6; + p2 = v2 + v5; + p5 = v2 - v5; + p3 = v3 + v4; + p4 = v3 - v4; + + // convert to 8-bit integers + p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? 255 : p0 >> 4; + p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? 255 : p1 >> 4; + p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? 255 : p2 >> 4; + p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? 255 : p3 >> 4; + p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? 255 : p4 >> 4; + p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? 255 : p5 >> 4; + p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? 255 : p6 >> 4; + p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? 255 : p7 >> 4; + + // store block data + blockData[blockBufferOffset + col] = p0; + blockData[blockBufferOffset + col + 8] = p1; + blockData[blockBufferOffset + col + 16] = p2; + blockData[blockBufferOffset + col + 24] = p3; + blockData[blockBufferOffset + col + 32] = p4; + blockData[blockBufferOffset + col + 40] = p5; + blockData[blockBufferOffset + col + 48] = p6; + blockData[blockBufferOffset + col + 56] = p7; + } + } + + function buildComponentData(frame, component) { + var blocksPerLine = component.blocksPerLine; + var blocksPerColumn = component.blocksPerColumn; + var computationBuffer = new Int16Array(64); + console.log("qt"); + console.log(component.quantizationTable); + for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) { + for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) { + var offset = getBlockBufferOffset(component, blockRow, blockCol); + quantizeAndInverse(component, offset, computationBuffer); + } + } + + console.log("component.blockData"); + console.log(component.blockData); + return component.blockData; + } + + function clamp0to255(a) { + return a <= 0 ? 0 : a >= 255 ? 255 : a; + } + + function findNextFileMarker(data, currentPos, startPos) { + function peekUint16(pos) { + return (data[pos] << 8) | data[pos + 1]; + } + + var maxPos = data.length - 1; + var newPos = startPos < currentPos ? startPos : currentPos; + + if (currentPos >= maxPos) { + return null; // Don't attempt to read non-existent data and just return. + } + var currentMarker = peekUint16(currentPos); + if (currentMarker >= 0xFFC0 && currentMarker <= 0xFFFE) { + return { + invalid: null, + marker: currentMarker, + offset: currentPos, + }; + } + var newMarker = peekUint16(newPos); + while (!(newMarker >= 0xFFC0 && newMarker <= 0xFFFE)) { + if (++newPos >= maxPos) { + return null; // Don't attempt to read non-existent data and just return. + } + newMarker = peekUint16(newPos); + } + return { + invalid: currentMarker.toString(16), + marker: newMarker, + offset: newPos, + }; + } + + JpegImage.prototype = { + parse: function parse(data) { + + function readUint16() { + var value = (data[offset] << 8) | data[offset + 1]; + offset += 2; + return value; + } + + function readDataBlock() { + var length = readUint16(); + var endOffset = offset + length - 2; + + var fileMarker = findNextFileMarker(data, endOffset, offset); + if (fileMarker && fileMarker.invalid) { + warn('readDataBlock - incorrect length, next marker is: ' + + fileMarker.invalid); + endOffset = fileMarker.offset; + } + + var array = data.subarray(offset, endOffset); + offset += array.length; + return array; + } + + function prepareComponents(frame) { + var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH); + var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV); + for (var i = 0; i < frame.components.length; i++) { + component = frame.components[i]; + var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * + component.h / frame.maxH); + var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * + component.v / frame.maxV); + var blocksPerLineForMcu = mcusPerLine * component.h; + var blocksPerColumnForMcu = mcusPerColumn * component.v; + + var blocksBufferSize = 64 * blocksPerColumnForMcu * + (blocksPerLineForMcu + 1); + component.blockData = new Int16Array(blocksBufferSize); + component.blocksPerLine = blocksPerLine; + component.blocksPerColumn = blocksPerColumn; + } + frame.mcusPerLine = mcusPerLine; + frame.mcusPerColumn = mcusPerColumn; + } + + var offset = 0; + var jfif = null; + var adobe = null; + var frame, resetInterval; + var quantizationTables = []; + var huffmanTablesAC = [], huffmanTablesDC = []; + var fileMarker = readUint16(); + if (fileMarker !== 0xFFD8) { // SOI (Start of Image) + error('JPEG error: SOI not found'); + } + + fileMarker = readUint16(); + while (fileMarker !== 0xFFD9) { // EOI (End of image) + var i, j, l; + switch (fileMarker) { + case 0xFFE0: // APP0 (Application Specific) + case 0xFFE1: // APP1 + case 0xFFE2: // APP2 + case 0xFFE3: // APP3 + case 0xFFE4: // APP4 + case 0xFFE5: // APP5 + case 0xFFE6: // APP6 + case 0xFFE7: // APP7 + case 0xFFE8: // APP8 + case 0xFFE9: // APP9 + case 0xFFEA: // APP10 + case 0xFFEB: // APP11 + case 0xFFEC: // APP12 + case 0xFFED: // APP13 + case 0xFFEE: // APP14 + case 0xFFEF: // APP15 + case 0xFFFE: // COM (Comment) + var appData = readDataBlock(); + + if (fileMarker === 0xFFE0) { + if (appData[0] === 0x4A && appData[1] === 0x46 && + appData[2] === 0x49 && appData[3] === 0x46 && + appData[4] === 0) { // 'JFIF\x00' + jfif = { + version: { major: appData[5], minor: appData[6], }, + densityUnits: appData[7], + xDensity: (appData[8] << 8) | appData[9], + yDensity: (appData[10] << 8) | appData[11], + thumbWidth: appData[12], + thumbHeight: appData[13], + thumbData: appData.subarray(14, 14 + + 3 * appData[12] * appData[13]), + }; + } + } + // TODO APP1 - Exif + if (fileMarker === 0xFFEE) { + if (appData[0] === 0x41 && appData[1] === 0x64 && + appData[2] === 0x6F && appData[3] === 0x62 && + appData[4] === 0x65) { // 'Adobe' + adobe = { + version: (appData[5] << 8) | appData[6], + flags0: (appData[7] << 8) | appData[8], + flags1: (appData[9] << 8) | appData[10], + transformCode: appData[11], + }; + } + } + break; + + case 0xFFDB: // DQT (Define Quantization Tables) + var quantizationTablesLength = readUint16(); + var quantizationTablesEnd = quantizationTablesLength + offset - 2; + var z; + while (offset < quantizationTablesEnd) { + var quantizationTableSpec = data[offset++]; + var tableData = new Uint16Array(64); + if ((quantizationTableSpec >> 4) === 0) { // 8 bit values + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = data[offset++]; + } + } else if ((quantizationTableSpec >> 4) === 1) { // 16 bit values + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = readUint16(); + } + } else { + error('JPEG error: DQT - invalid table spec'); + } + quantizationTables[quantizationTableSpec & 15] = tableData; + } + break; + + case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT) + case 0xFFC1: // SOF1 (Start of Frame, Extended DCT) + case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT) + if (frame) { + error('JPEG error: Only single frame JPEGs supported'); + } + console.log("filemarker"); + console.log(fileMarker); + console.log(offset); + readUint16(); // skip data length + frame = {}; + frame.extended = (fileMarker === 0xFFC1); + frame.progressive = (fileMarker === 0xFFC2); + frame.precision = data[offset++]; + frame.scanLines = readUint16(); + frame.samplesPerLine = readUint16(); + frame.components = []; + frame.componentIds = {}; + var componentsCount = data[offset++], componentId; + var maxH = 0, maxV = 0; + for (i = 0; i < componentsCount; i++) { + componentId = data[offset]; + var h = data[offset + 1] >> 4; + var v = data[offset + 1] & 15; + if (maxH < h) { + maxH = h; + } + if (maxV < v) { + maxV = v; + } + var qId = data[offset + 2]; + l = frame.components.push({ + h, + v, + quantizationId: qId, + quantizationTable: null, // See comment below. + }); + frame.componentIds[componentId] = l - 1; + offset += 3; + } + + frame.maxH = maxH; + frame.maxV = maxV; + prepareComponents(frame); + break; + + case 0xFFC4: // DHT (Define Huffman Tables) + var huffmanLength = readUint16(); + for (i = 2; i < huffmanLength;) { + console.log("offset= " + offset); + var huffmanTableSpec = data[offset++]; + console.log("huffmanTableSpec= " + huffmanTableSpec); + + var codeLengths = new Uint8Array(16); + var codeLengthSum = 0; + for (j = 0; j < 16; j++, offset++) { + codeLengthSum += (codeLengths[j] = data[offset]); + } + console.log("codelengthsum = " + codeLengthSum); + console.log("offset = " + offset); + var huffmanValues = new Uint8Array(codeLengthSum); + for (j = 0; j < codeLengthSum; j++, offset++) { + huffmanValues[j] = data[offset]; + } + i += 17 + codeLengthSum; + + + console.log((huffmanTableSpec >> 4) === 0 ? "DC":"AC"); + ((huffmanTableSpec >> 4) === 0 + ? huffmanTablesDC + : huffmanTablesAC)[huffmanTableSpec & 15] = + buildHuffmanTable(codeLengths, huffmanValues); + } + break; + + case 0xFFDD: // DRI (Define Restart Interval) + readUint16(); // skip data length + resetInterval = readUint16(); + break; + + case 0xFFDA: // SOS (Start of Scan) + readUint16(); // scanLength + var selectorsCount = data[offset++]; + var components = [], component; + for (i = 0; i < selectorsCount; i++) { + var ci = data[offset++]; + console.log("ci= " + ci); + console.log("offset= " + offset); + + var componentIndex = frame.componentIds[ci]; + console.log("componentIndex= " + componentIndex); + component = frame.components[componentIndex]; + var tableSpec = data[offset++]; + component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4]; + component.huffmanTableAC = huffmanTablesAC[tableSpec & 15]; + components.push(component); + } + console.log("components= " + components); + + var spectralStart = data[offset++]; + var spectralEnd = data[offset++]; + var successiveApproximation = data[offset++]; + + console.log(frame.componentIds); + console.log("spectralStart= " + spectralStart); + console.log("spectralEnd= " + spectralEnd); + console.log("successiveApproximation= " + successiveApproximation); + // console.log("components before") + // console.log(components) + var processed = decodeScan(data, offset, + frame, components, resetInterval, + spectralStart, spectralEnd, + successiveApproximation >> 4, successiveApproximation & 15); + offset += processed; + console.log("components after"); + // console.log(frame); + for (var i = 0; i < 3; i++){ + for (var j = 0; j < 10; j++){ + console.log("component ["+ i +"] : value ["+j+"] ="+ frame.components[i].blockData[j]+"]"); + } + } + break; + + case 0xFFFF: // Fill bytes + if (data[offset] !== 0xFF) { // Avoid skipping a valid marker. + offset--; + } + break; + + default: + if (data[offset - 3] === 0xFF && + data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) { + // could be incorrect encoding -- last 0xFF byte of the previous + // block was eaten by the encoder + offset -= 3; + break; + } + + // TODO: Delete this after testing + fileMarker = 0xFFD9; + // error('JPEG error: unknown marker ' + fileMarker.toString(16)); + } + fileMarker = readUint16(); + } + + console.log("quantizationTables"); + console.log(quantizationTables); + + this.width = frame.samplesPerLine; + this.height = frame.scanLines; + this.jfif = jfif; + this.adobe = adobe; + this.components = []; + for (i = 0; i < frame.components.length; i++) { + component = frame.components[i]; + + // Prevent errors when DQT markers are placed after SOF{n} markers, + // by assigning the `quantizationTable` entry after the entire image + // has been parsed (fixes issue7406.pdf). + var quantizationTable = quantizationTables[component.quantizationId]; + if (quantizationTable) { + component.quantizationTable = quantizationTable; + } + + this.components.push({ + output: buildComponentData(frame, component), + scaleX: component.h / frame.maxH, + scaleY: component.v / frame.maxV, + blocksPerLine: component.blocksPerLine, + blocksPerColumn: component.blocksPerColumn, + }); + } + + console.log("components"); + console.log(this.components); + this.numComponents = this.components.length; + }, + + _getLinearizedBlockData: function getLinearizedBlockData(width, height) { + var scaleX = this.width / width, scaleY = this.height / height; + + var component, componentScaleX, componentScaleY, blocksPerScanline; + var x, y, i, j, k; + var index; + var offset = 0; + var output; + var numComponents = this.components.length; + var dataLength = width * height * numComponents; + var data = new Uint8Array(dataLength); + var xScaleBlockOffset = new Uint32Array(width); + var mask3LSB = 0xfffffff8; // used to clear the 3 LSBs + + for (i = 0; i < numComponents; i++) { + component = this.components[i]; + componentScaleX = component.scaleX * scaleX; + componentScaleY = component.scaleY * scaleY; + offset = i; + output = component.output; + blocksPerScanline = (component.blocksPerLine + 1) << 3; + // precalculate the xScaleBlockOffset + for (x = 0; x < width; x++) { + j = 0 | (x * componentScaleX); + xScaleBlockOffset[x] = ((j & mask3LSB) << 3) | (j & 7); + } + // linearize the blocks of the component + for (y = 0; y < height; y++) { + j = 0 | (y * componentScaleY); + index = blocksPerScanline * (j & mask3LSB) | ((j & 7) << 3); + for (x = 0; x < width; x++) { + data[offset] = output[index + xScaleBlockOffset[x]]; + offset += numComponents; + } + } + } + + // decodeTransform contains pairs of multiplier (-256..256) and additive + var transform = this.decodeTransform; + if (transform) { + for (i = 0; i < dataLength;) { + for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) { + data[i] = ((data[i] * transform[k]) >> 8) + transform[k + 1]; + } + } + } + return data; + }, + + _isColorConversionNeeded: function isColorConversionNeeded() { + if (this.adobe && this.adobe.transformCode) { + // The adobe transform marker overrides any previous setting + return true; + } else if (this.numComponents === 3) { + if (!this.adobe && this.colorTransform === 0) { + // If the Adobe transform marker is not present and the image + // dictionary has a 'ColorTransform' entry, explicitly set to `0`, + // then the colours should *not* be transformed. + return false; + } + return true; + } + // `this.numComponents !== 3` + if (!this.adobe && this.colorTransform === 1) { + // If the Adobe transform marker is not present and the image + // dictionary has a 'ColorTransform' entry, explicitly set to `1`, + // then the colours should be transformed. + return true; + } + return false; + }, + + _convertYccToRgb: function convertYccToRgb(data) { + var Y, Cb, Cr; + for (var i = 0, length = data.length; i < length; i += 3) { + Y = data[i ]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i ] = clamp0to255(Y - 179.456 + 1.402 * Cr); + data[i + 1] = clamp0to255(Y + 135.459 - 0.344 * Cb - 0.714 * Cr); + data[i + 2] = clamp0to255(Y - 226.816 + 1.772 * Cb); + } + return data; + }, + + _convertYcckToRgb: function convertYcckToRgb(data) { + var Y, Cb, Cr, k; + var offset = 0; + for (var i = 0, length = data.length; i < length; i += 4) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + k = data[i + 3]; + + var r = -122.67195406894 + + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - + 5.4080610064599e-5 * Y + 0.00048449797120281 * k - + 0.154362151871126) + + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - + 0.00477271405408747 * k + 1.53380253221734) + + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + + 0.48357088451265) + + k * (-0.000336197177618394 * k + 0.484791561490776); + + var g = 107.268039397724 + + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + + 0.000659397001245577 * Y + 0.000426105652938837 * k - + 0.176491792462875) + + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + + 0.000770482631801132 * k - 0.151051492775562) + + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + + 0.25802910206845) + + k * (-0.000318913117588328 * k - 0.213742400323665); + + var b = -20.810012546947 + + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + + 0.0020741088115012 * Y - 0.00288260236853442 * k + + 0.814272968359295) + + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + + 0.000560833691242812 * k - 0.195152027534049) + + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + + 0.116935020465145) + + k * (-0.000343531996510555 * k + 0.24165260232407); + + data[offset++] = clamp0to255(r); + data[offset++] = clamp0to255(g); + data[offset++] = clamp0to255(b); + } + return data; + }, + + _convertYcckToCmyk: function convertYcckToCmyk(data) { + var Y, Cb, Cr; + for (var i = 0, length = data.length; i < length; i += 4) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i ] = clamp0to255(434.456 - Y - 1.402 * Cr); + data[i + 1] = clamp0to255(119.541 - Y + 0.344 * Cb + 0.714 * Cr); + data[i + 2] = clamp0to255(481.816 - Y - 1.772 * Cb); + // K in data[i + 3] is unchanged + } + return data; + }, + + _convertCmykToRgb: function convertCmykToRgb(data) { + var c, m, y, k; + var offset = 0; + var min = -255 * 255 * 255; + var scale = 1 / 255 / 255; + for (var i = 0, length = data.length; i < length; i += 4) { + c = data[i]; + m = data[i + 1]; + y = data[i + 2]; + k = data[i + 3]; + + var r = + c * (-4.387332384609988 * c + 54.48615194189176 * m + + 18.82290502165302 * y + 212.25662451639585 * k - + 72734.4411664936) + + m * (1.7149763477362134 * m - 5.6096736904047315 * y - + 17.873870861415444 * k - 1401.7366389350734) + + y * (-2.5217340131683033 * y - 21.248923337353073 * k + + 4465.541406466231) - + k * (21.86122147463605 * k + 48317.86113160301); + var g = + c * (8.841041422036149 * c + 60.118027045597366 * m + + 6.871425592049007 * y + 31.159100130055922 * k - + 20220.756542821975) + + m * (-15.310361306967817 * m + 17.575251261109482 * y + + 131.35250912493976 * k - 48691.05921601825) + + y * (4.444339102852739 * y + 9.8632861493405 * k - + 6341.191035517494) - + k * (20.737325471181034 * k + 47890.15695978492); + var b = + c * (0.8842522430003296 * c + 8.078677503112928 * m + + 30.89978309703729 * y - 0.23883238689178934 * k - + 3616.812083916688) + + m * (10.49593273432072 * m + 63.02378494754052 * y + + 50.606957656360734 * k - 28620.90484698408) + + y * (0.03296041114873217 * y + 115.60384449646641 * k - + 49363.43385999684) - + k * (22.33816807309886 * k + 45932.16563550634); + + data[offset++] = r >= 0 ? 255 : r <= min ? 0 : 255 + r * scale | 0; + data[offset++] = g >= 0 ? 255 : g <= min ? 0 : 255 + g * scale | 0; + data[offset++] = b >= 0 ? 255 : b <= min ? 0 : 255 + b * scale | 0; + } + return data; + }, + + getData: function getData(width, height, forceRGBoutput) { + if (this.numComponents > 4) { + error('JPEG error: Unsupported color mode'); + } + // type of data: Uint8Array(width * height * numComponents) + var data = this._getLinearizedBlockData(width, height); + + if (this.numComponents === 1 && forceRGBoutput) { + var dataLength = data.length; + var rgbData = new Uint8Array(dataLength * 3); + var offset = 0; + for (var i = 0; i < dataLength; i++) { + var grayColor = data[i]; + rgbData[offset++] = grayColor; + rgbData[offset++] = grayColor; + rgbData[offset++] = grayColor; + } + return rgbData; + } else if (this.numComponents === 3 && this._isColorConversionNeeded()) { + return this._convertYccToRgb(data); + } else if (this.numComponents === 4) { + if (this._isColorConversionNeeded()) { + if (forceRGBoutput) { + return this._convertYcckToRgb(data); + } + return this._convertYcckToCmyk(data); + } else if (forceRGBoutput) { + return this._convertCmykToRgb(data); + } + } + return data; + }, + }; + + return JpegImage; +})(); + +// export { +// JpegImage, +// }; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 6d82b53746..248e0a5eea 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,46 +1,147 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Text; - using Xunit; +using System.IO; +using System.IO.Compression; +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +// ReSharper disable InconsistentNaming - using ImageSharp.Formats; - using ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + // TODO: Fix all bugs, and re enable Skipped and commented stuff !!! public class PngDecoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; - public static readonly string[] TestFiles = + public static readonly string[] CommonTestImages = + { + TestImages.Png.Splash, TestImages.Png.Indexed, + TestImages.Png.FilterVar, + TestImages.Png.Bad.ChunkLength1, + TestImages.Png.Bad.CorruptedChunk, + + TestImages.Png.VimImage1, + TestImages.Png.VersioningImage1, + TestImages.Png.VersioningImage2, + + TestImages.Png.SnakeGame, + TestImages.Png.Banner7Adam7InterlaceMode, + TestImages.Png.Banner8Index, + }; + + + public static readonly string[] TestImages48Bpp = + { + TestImages.Png.Rgb48Bpp, + TestImages.Png.Rgb48BppInterlaced + }; + + // This is a workaround for Mono-s decoder being incompatible with ours and GDI+. + // We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA! + public static readonly string[] WindowsOnlyTestImages = { - TestImages.Png.Splash, TestImages.Png.Indexed, TestImages.Png.Interlaced, TestImages.Png.FilterVar, - TestImages.Png.Bad.ChunkLength1, TestImages.Png.Bad.ChunkLength2, TestImages.Png.Rgb48Bpp + TestImages.Png.Bad.ChunkLength2, + TestImages.Png.VimImage2, }; [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes)] - public void DecodeAndReSave(TestImageProvider imageProvider) + [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] + public void Decode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + + // This is a workaround for Mono-s decoder being incompatible with ours and GDI+. + // We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA! + [Theory] + [WithFileCollection(nameof(WindowsOnlyTestImages), PixelTypes.Rgba32)] + public void Decode_WindowsOnlyTestImages(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + + if (!TestEnvironment.IsLinux) + { + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + } + + [Theory] + [WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)] + public void Decode_Interlaced_DoesNotThrow(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)] + public void Decode_Interlaced_ImageIsCorrect(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + + // TODO: We need to decode these into Rgba64 properly, and do 'CompareToOriginal' in a Rgba64 mode! (See #285) + [Theory] + [WithFileCollection(nameof(TestImages48Bpp), PixelTypes.Rgba32)] + public void Decode_48Bpp(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + + // Workaround a bug in mono-s System.Drawing PNG decoder. It can't deal with 48Bpp png-s :( + if (!TestEnvironment.IsLinux) + { + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + } + + [Theory] + [WithFile(TestImages.Png.Splash, PixelTypes)] + public void Decoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = imageProvider.GetImage()) + using (Image image = provider.GetImage(new PngDecoder())) { - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } [Fact] public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead() { - PngDecoder options = new PngDecoder() + var options = new PngDecoder() { IgnoreMetadata = false }; - TestFile testFile = TestFile.Create(TestImages.Png.Blur); + var testFile = TestFile.Create(TestImages.Png.Blur); using (Image image = testFile.CreateImage(options)) { @@ -53,12 +154,12 @@ namespace ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored() { - PngDecoder options = new PngDecoder() + var options = new PngDecoder() { IgnoreMetadata = true }; - TestFile testFile = TestFile.Create(TestImages.Png.Blur); + var testFile = TestFile.Create(TestImages.Png.Blur); using (Image image = testFile.CreateImage(options)) { @@ -69,12 +170,12 @@ namespace ImageSharp.Tests [Fact] public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() { - PngDecoder options = new PngDecoder() + var options = new PngDecoder() { TextEncoding = Encoding.Unicode }; - TestFile testFile = TestFile.Create(TestImages.Png.Blur); + var testFile = TestFile.Create(TestImages.Png.Blur); using (Image image = testFile.CreateImage(options)) { @@ -82,5 +183,84 @@ namespace ImageSharp.Tests Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); } } + + [Theory] + [InlineData(TestImages.Png.Bpp1, 1)] + [InlineData(TestImages.Png.Gray4Bpp, 4)] + [InlineData(TestImages.Png.Palette8Bpp, 8)] + [InlineData(TestImages.Png.Pd, 24)] + [InlineData(TestImages.Png.Blur, 32)] + [InlineData(TestImages.Png.Rgb48Bpp, 48)] + [InlineData(TestImages.Png.Rgb48BppInterlaced, 48)] + public void DetectPixelSize(string imagePath, int expectedPixelSize) + { + TestFile testFile = TestFile.Create(imagePath); + using (var stream = new MemoryStream(testFile.Bytes, false)) + { + Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); + } + } + + [Theory] + [InlineData(PngChunkTypes.Header)] + [InlineData(PngChunkTypes.Palette)] + // [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this + [InlineData(PngChunkTypes.End)] + public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(string chunkName) + { + using (var memStream = new MemoryStream()) + { + memStream.Skip(8); + + WriteChunk(memStream, chunkName); + + CompressStream(memStream); + + var decoder = new PngDecoder(); + + ImageFormatException exception = Assert.Throws(() => + { + decoder.Decode(null, memStream); + }); + + Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); + } + } + + [Theory] + [InlineData(PngChunkTypes.Gamma)] + [InlineData(PngChunkTypes.PaletteAlpha)] + [InlineData(PngChunkTypes.Physical)] + //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this + public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(string chunkName) + { + using (var memStream = new MemoryStream()) + { + memStream.Skip(8); + + WriteChunk(memStream, chunkName); + + CompressStream(memStream); + + var decoder = new PngDecoder(); + decoder.Decode(null, memStream); + } + } + + private static void WriteChunk(MemoryStream memStream, string chunkName) + { + memStream.Write(new byte[] { 0, 0, 0, 1 }, 0, 4); + memStream.Write(Encoding.GetEncoding("ASCII").GetBytes(chunkName), 0, 4); + memStream.Write(new byte[] { 0, 0, 0, 0, 0 }, 0, 5); + } + + private static void CompressStream(Stream stream) + { + stream.Position = 0; + using (var deflateStream = new DeflateStream(stream, CompressionLevel.NoCompression, true)) + { + } + stream.Position = 0; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 24907cfdb7..1566ddf442 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -using ImageSharp.Formats; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Threading.Tasks; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - - using Xunit; - public class PngEncoderTests : FileTestBase { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; @@ -31,10 +28,10 @@ namespace ImageSharp.Tests { using (Image image = provider.GetImage()) { - PngEncoder options = new PngEncoder() - { - PngColorType = pngColorType - }; + var options = new PngEncoder() + { + PngColorType = pngColorType + }; provider.Utility.TestName += "_" + pngColorType; provider.Utility.SaveTestOutputFile(image, "png", options); @@ -47,7 +44,7 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { using (Image image = provider.GetImage()) - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { image.Save(ms, new PngEncoder()); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index bac340a71c..fc17df93d1 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -1,22 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Formats.Png -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.IO; - using Xunit; - using ImageSharp.Formats; - using System.Linq; - using ImageSharp.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.IO; +using Xunit; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Formats.Png; +namespace SixLabors.ImageSharp.Tests.Formats.Png +{ public class PngSmokeTests { [Theory] @@ -34,31 +27,32 @@ namespace ImageSharp.Tests.Formats.Png ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { + ImageComparer.Tolerant().VerifySimilarity(image, img2); // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.CheckSimilarity(image, img2); } } } - [Theory] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] - public void CanSaveIndexedPng(TestImageProvider provider) - where TPixel : struct, IPixel - { - // does saving a file then repoening mean both files are identical??? - using (Image image = provider.GetImage()) - using (MemoryStream ms = new MemoryStream()) - { - // image.Save(provider.Utility.GetTestOutputFileName("bmp")); - image.Save(ms, new PngEncoder() { PaletteSize = 256 }); - ms.Position = 0; - using (Image img2 = Image.Load(ms, new PngDecoder())) - { - // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.CheckSimilarity(image, img2, 0.03f); - } - } - } + // JJS: Disabled for now as the decoder now correctly decodes the full pixel components if the + // paletted image has alpha of 0 + //[Theory] + //[WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + //public void CanSaveIndexedPng(TestImageProvider provider) + // where TPixel : struct, IPixel + //{ + // // does saving a file then repoening mean both files are identical??? + // using (Image image = provider.GetImage()) + // using (MemoryStream ms = new MemoryStream()) + // { + // // image.Save(provider.Utility.GetTestOutputFileName("bmp")); + // image.Save(ms, new PngEncoder() { PaletteSize = 256 }); + // ms.Position = 0; + // using (Image img2 = Image.Load(ms, new PngDecoder())) + // { + // ImageComparer.VerifySimilarity(image, img2, 0.03f); + // } + // } + //} // JJS: Commented out for now since the test does not take into lossy nature of indexing. //[Theory] @@ -113,14 +107,14 @@ namespace ImageSharp.Tests.Formats.Png using (MemoryStream ms = new MemoryStream()) { // image.Save(provider.Utility.GetTestOutputFileName("png")); - image.Resize(100, 100); + image.Mutate(x => x.Resize(100, 100)); // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); image.Save(ms, new PngEncoder()); ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { - ImageComparer.CheckSimilarity(image, img2); + ImageComparer.Tolerant().VerifySimilarity(image, img2); } } } diff --git a/tests/ImageSharp.Tests/GlobalSuppressions.cs b/tests/ImageSharp.Tests/GlobalSuppressions.cs new file mode 100644 index 0000000000..3d161049b9 --- /dev/null +++ b/tests/ImageSharp.Tests/GlobalSuppressions.cs @@ -0,0 +1,10 @@ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1013:Public method should be marked as test")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Assertions", "xUnit2013:Do not use equality check to check for collection size.")] + diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs index ba6d5687ce..83075dc83e 100644 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ b/tests/ImageSharp.Tests/Helpers/GuardTests.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Helpers -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using Xunit; +using System; +using System.Diagnostics.CodeAnalysis; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Helpers +{ /// /// Tests the helper. /// diff --git a/tests/ImageSharp.Tests/Helpers/MathFTests.cs b/tests/ImageSharp.Tests/Helpers/MathFTests.cs deleted file mode 100644 index 62a971f9f5..0000000000 --- a/tests/ImageSharp.Tests/Helpers/MathFTests.cs +++ /dev/null @@ -1,121 +0,0 @@ -namespace ImageSharp.Tests.Helpers -{ - using System; - - using Xunit; - - public class MathFTests - { - [Fact] - public void MathF_PI_Is_Equal() - { - Assert.Equal(MathF.PI, (float)Math.PI); - } - - [Fact] - public void MathF_Ceililng_Is_Equal() - { - Assert.Equal(MathF.Ceiling(0.3333F), (float)Math.Ceiling(0.3333F)); - } - - [Fact] - public void MathF_Cos_Is_Equal() - { - Assert.Equal(MathF.Cos(0.3333F), (float)Math.Cos(0.3333F)); - } - - [Fact] - public void MathF_Abs_Is_Equal() - { - Assert.Equal(MathF.Abs(-0.3333F), (float)Math.Abs(-0.3333F)); - } - - [Fact] - public void MathF_Atan2_Is_Equal() - { - Assert.Equal(MathF.Atan2(1.2345F, 1.2345F), (float)Math.Atan2(1.2345F, 1.2345F)); - } - - [Fact] - public void MathF_Exp_Is_Equal() - { - Assert.Equal(MathF.Exp(1.2345F), (float)Math.Exp(1.2345F)); - } - - [Fact] - public void MathF_Floor_Is_Equal() - { - Assert.Equal(MathF.Floor(1.2345F), (float)Math.Floor(1.2345F)); - } - - [Fact] - public void MathF_Min_Is_Equal() - { - Assert.Equal(MathF.Min(1.2345F, 5.4321F), (float)Math.Min(1.2345F, 5.4321F)); - } - - [Fact] - public void MathF_Max_Is_Equal() - { - Assert.Equal(MathF.Max(1.2345F, 5.4321F), (float)Math.Max(1.2345F, 5.4321F)); - } - - [Fact] - public void MathF_Pow_Is_Equal() - { - Assert.Equal(MathF.Pow(1.2345F, 5.4321F), (float)Math.Pow(1.2345F, 5.4321F)); - } - - [Fact] - public void MathF_Round_Is_Equal() - { - Assert.Equal(MathF.Round(1.2345F), (float)Math.Round(1.2345F)); - } - - [Fact] - public void MathF_Round_With_Midpoint_Is_Equal() - { - Assert.Equal(MathF.Round(1.2345F, MidpointRounding.AwayFromZero), (float)Math.Round(1.2345F, MidpointRounding.AwayFromZero)); - } - - [Fact] - public void MathF_Sin_Is_Equal() - { - Assert.Equal(MathF.Sin(1.2345F), (float)Math.Sin(1.2345F)); - } - - [Fact] - public void MathF_SinC_Is_Equal() - { - float f = 1.2345F; - float expected = 1F; - if (Math.Abs(f) > Constants.Epsilon) - { - f *= (float)Math.PI; - float sinC = (float)Math.Sin(f) / f; - - expected = Math.Abs(sinC) < Constants.Epsilon ? 0F : sinC; - } - - Assert.Equal(MathF.SinC(1.2345F), expected); - } - - [Fact] - public void MathF_Sqrt_Is_Equal() - { - Assert.Equal(MathF.Sqrt(2F), (float)Math.Sqrt(2F)); - } - - [Fact] - public void Convert_Degree_To_Radian() - { - Assert.Equal((float)(Math.PI / 2D), MathF.DegreeToRadian(90F), new FloatRoundingComparer(6)); - } - - [Fact] - public void Convert_Radian_To_Degree() - { - Assert.Equal(60F, MathF.RadianToDegree((float)(Math.PI / 3D)), new FloatRoundingComparer(5)); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs index 4cdf9122a9..254cfa2dc4 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs index 06962e0106..d8408523ba 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using ImageSharp.IO; - using Xunit; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs index a69727d457..19ef24c79c 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// @@ -166,6 +164,8 @@ namespace ImageSharp.Tests.IO Assert.Equal(1099511627776L * 256 * 256, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0 }, 0)); Assert.Equal(-1L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 255, 255, 255, 255, 255, 255, 255, 255 }, 0)); Assert.Equal(257L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 0, 0, 0, 0, 0, 0, 1, 1 }, 0)); + Assert.Equal(4294967295L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 0, 0, 0, 0, 255, 255, 255, 255 }, 0)); + Assert.Equal(-4294967296L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 255, 255, 255, 255, 0, 0, 0, 0 }, 0)); Assert.Equal(0L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 0 }, 1)); Assert.Equal(1L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 1 }, 1)); @@ -178,6 +178,8 @@ namespace ImageSharp.Tests.IO Assert.Equal(1099511627776L * 256 * 256, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 0, 1, 0, 0, 0, 0, 0, 0, 0 }, 1)); Assert.Equal(-1L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 0, 255, 255, 255, 255, 255, 255, 255, 255 }, 1)); Assert.Equal(257L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 0, 0, 1, 1 }, 1)); + Assert.Equal(4294967295L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 255, 255, 255, 255 }, 1)); + Assert.Equal(-4294967296L, EndianBitConverter.BigEndianConverter.ToInt64(new byte[] { 1, 255, 255, 255, 255, 0, 0, 0, 0 }, 1)); } /// diff --git a/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs b/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs index ffd9cdedcc..87adead338 100644 --- a/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs +++ b/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using System.IO; - using System.Text; - - using ImageSharp.IO; - - using Xunit; +using System; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The endian binary reader tests. /// @@ -49,8 +45,7 @@ namespace ImageSharp.Tests.IO [Fact] public void ReadCharsBeyondProvidedBufferSize() { - Assert.Throws( - typeof(ArgumentException), + Assert.Throws( () => { MemoryStream stream = new MemoryStream(TestBytes); diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs index 5ff47409b0..97d9275ad1 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs index 7fd7a97d43..eae8ca2919 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using ImageSharp.IO; - using Xunit; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs index c46c011a14..0e09d1d071 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// @@ -164,6 +162,8 @@ namespace ImageSharp.Tests.IO Assert.Equal(1099511627776L * 256 * 256, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 0, 0, 0, 0, 0, 0, 1 }, 0)); Assert.Equal(-1L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 255, 255, 255, 255, 255, 255, 255, 255 }, 0)); Assert.Equal(257L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 1, 1, 0, 0, 0, 0, 0, 0 }, 0)); + Assert.Equal(4294967295L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 255, 255, 255, 255, 0, 0, 0, 0 }, 0)); + Assert.Equal(-4294967296L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 0, 0, 0, 255, 255, 255, 255 }, 0)); Assert.Equal(0L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 0 }, 1)); Assert.Equal(1L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 1, 0, 0, 0, 0, 0, 0, 0 }, 1)); @@ -176,6 +176,8 @@ namespace ImageSharp.Tests.IO Assert.Equal(1099511627776L * 256 * 256, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 1 }, 1)); Assert.Equal(-1L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 255, 255, 255, 255, 255, 255, 255, 255 }, 1)); Assert.Equal(257L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 1, 1, 0, 0, 0, 0, 0, 0 }, 1)); + Assert.Equal(4294967295L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 255, 255, 255, 255, 0, 0, 0, 0 }, 1)); + Assert.Equal(-4294967296L, EndianBitConverter.LittleEndianConverter.ToInt64(new byte[] { 0, 0, 0, 0, 0, 255, 255, 255, 255 }, 1)); } /// diff --git a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs index 472d643cd3..3fa94d6711 100644 --- a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs +++ b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using System.IO; - using ImageSharp.IO; - - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ public class LocalFileSystemTests { [Fact] diff --git a/tests/ImageSharp.Tests/Image/ImageCloneTests.cs b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs new file mode 100644 index 0000000000..12e0fc8834 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageCloneTests.cs @@ -0,0 +1,36 @@ +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageCloneTests + { + [Theory] + [WithTestPatternImages(9, 9, PixelTypes.Rgba32)] + public void CloneAs_ToBgra32(TestImageProvider provider) + { + using (Image image = provider.GetImage()) + using (Image clone = image.CloneAs()) + { + for (int y = 0; y < image.Height; y++) + { + Span row = image.GetPixelRowSpan(y); + Span rowClone = clone.GetPixelRowSpan(y); + + for (int x = 0; x < image.Width; x++) + { + Rgba32 rgba = row[x]; + Bgra32 bgra = rowClone[x]; + + Assert.Equal(rgba.R, bgra.R); + Assert.Equal(rgba.G, bgra.G); + Assert.Equal(rgba.B, bgra.B); + Assert.Equal(rgba.A, bgra.A); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index 59a39c4542..aefa32f469 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - using Moq; - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// @@ -41,20 +38,20 @@ namespace ImageSharp.Tests this.fileSystem = new Mock(); - this.LocalConfiguration = new Configuration() + this.LocalConfiguration = new Configuration { FileSystem = this.fileSystem.Object }; this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object); - TestFormat.RegisterGloablTestFormat(); + TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker); this.FilePath = Guid.NewGuid().ToString(); this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream); - TestFileSystem.RegisterGloablTestFormat(); + TestFileSystem.RegisterGlobalTestFormat(); TestFileSystem.Global.AddFile(this.FilePath, this.DataStream); } @@ -86,7 +83,6 @@ namespace ImageSharp.Tests Assert.Equal(localImageFormat, type); } - [Fact] public void DiscoverImageFormatStream() { diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs new file mode 100644 index 0000000000..afae9cae8c --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageFramesCollectionTests : IDisposable + { + private Image image; + private ImageFrameCollection collection; + + public ImageFramesCollectionTests() + { + this.image = new Image(10, 10); + this.collection = new ImageFrameCollection(this.image, 10, 10); + } + + [Fact] + public void ImageFramesaLwaysHaveOneFrame() + { + Assert.Equal(1, this.collection.Count); + } + + [Fact] + public void AddNewFrame_FramesMustHaveSameSize() + { + ArgumentException ex = Assert.Throws(() => + { + this.collection.AddFrame(new ImageFrame(1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void AddNewFrame_Frame_FramesNotBeNull() + { + + ArgumentNullException ex = Assert.Throws(() => + { + this.collection.AddFrame((ImageFrame)null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void AddNewFrame_PixelBuffer_FramesNotBeNull() + { + + ArgumentNullException ex = Assert.Throws(() => + { + this.collection.AddFrame((Rgba32[])null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void AddNewFrame_PixelBuffer_BufferIncorrectSize() + { + + ArgumentOutOfRangeException ex = Assert.Throws(() => + { + this.collection.AddFrame(new Rgba32[0]); + }); + + Assert.StartsWith("Value must be greater than or equal to 100.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesMustHaveSameSize() + { + + ArgumentException ex = Assert.Throws(() => + { + this.collection.InsertFrame(1, new ImageFrame(1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesNotBeNull() + { + + ArgumentNullException ex = Assert.Throws(() => + { + this.collection.InsertFrame(1, null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void Constructor_FramesMustHaveSameSize() + { + + ArgumentException ex = Assert.Throws(() => + { + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10), + new ImageFrame(1,1), + }); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_ThrowIfRemovingLastFrame() + { + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10) + }); + + InvalidOperationException ex = Assert.Throws(() => + { + collection.RemoveFrame(0); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.RemoveFrame(0); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void RootFrameIsFrameAtIndexZero() + { + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + Assert.Equal(collection.RootFrame, collection[0]); + } + + [Fact] + public void ConstructorPopulatesFrames() + { + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + Assert.Equal(2, collection.Count); + } + + [Fact] + public void DisposeClearsCollection() + { + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.Dispose(); + + Assert.Equal(0, collection.Count); + } + + [Fact] + public void Dispose_DisposesAllInnerFrames() + { + var collection = new ImageFrameCollection(this.image, new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); + collection.Dispose(); + + Assert.All(framesSnapShot, f => + { + // the pixel source of the frame is null after its been disposed. + Assert.Null(f.PixelBuffer); + }); + } + + [Theory] + [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] + public void CloneFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + img.Frames.AddFrame(new ImageFrame(10, 10));// add a frame anyway + using (Image cloned = img.Frames.CloneFrame(0)) + { + Assert.Equal(2, img.Frames.Count); + cloned.ComparePixelBufferTo(img.GetPixelSpan()); + } + } + } + + [Theory] + [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] + public void ExtractFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + var sourcePixelData = img.GetPixelSpan().ToArray(); + + img.Frames.AddFrame(new ImageFrame(10, 10)); + using (Image cloned = img.Frames.ExportFrame(0)) + { + Assert.Equal(1, img.Frames.Count); + cloned.ComparePixelBufferTo(sourcePixelData); + } + } + } + + [Fact] + public void CreateFrame() + { + this.image.Frames.CreateFrame(); + Assert.Equal(2, this.image.Frames.Count); + } + + [Fact] + public void AddFrameFromPixelData() + { + var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); + this.image.Frames.AddFrame(pixelData); + Assert.Equal(2, this.image.Frames.Count); + } + + [Fact] + public void AddFrame_clones_sourceFrame() + { + var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); + var otherFRame = new ImageFrame(10, 10); + var addedFrame = this.image.Frames.AddFrame(otherFRame); + addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); + Assert.NotEqual(otherFRame, addedFrame); + } + + [Fact] + public void InsertFrame_clones_sourceFrame() + { + var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); + var otherFRame = new ImageFrame(10, 10); + var addedFrame = this.image.Frames.InsertFrame(0, otherFRame); + addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); + Assert.NotEqual(otherFRame, addedFrame); + } + + [Fact] + public void MoveFrame_LeavesFrmaeInCorrectLocation() + { + for (var i = 0; i < 9; i++) + { + this.image.Frames.CreateFrame(); + } + + var frame = this.image.Frames[4]; + this.image.Frames.MoveFrame(4, 7); + var newIndex = this.image.Frames.IndexOf(frame); + Assert.Equal(7, newIndex); + } + + + [Fact] + public void IndexOf_ReturnsCorrectIndex() + { + for (var i = 0; i < 9; i++) + { + this.image.Frames.CreateFrame(); + } + + var frame = this.image.Frames[4]; + var index = this.image.Frames.IndexOf(frame); + Assert.Equal(4, index); + } + + [Fact] + public void Contains_TrueIfMember() + { + for (var i = 0; i < 9; i++) + { + this.image.Frames.CreateFrame(); + } + + var frame = this.image.Frames[4]; + Assert.True(this.image.Frames.Contains(frame)); + } + + [Fact] + public void Contains_TFalseIfNoneMember() + { + for (var i = 0; i < 9; i++) + { + this.image.Frames.CreateFrame(); + } + + var frame = new ImageFrame(10, 10); + Assert.False(this.image.Frames.Contains(frame)); + } + + public void Dispose() + { + this.image.Dispose(); + this.collection.Dispose(); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index bb64ceda34..2c0a30b154 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - using Moq; - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using Moq; +using Xunit; +using SixLabors.ImageSharp.Advanced; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// @@ -44,7 +41,8 @@ namespace ImageSharp.Tests this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) - .Callback((c, s) => { + .Callback((c, s) => + { using (var ms = new MemoryStream()) { s.CopyTo(ms); @@ -55,28 +53,28 @@ namespace ImageSharp.Tests this.fileSystem = new Mock(); - this.LocalConfiguration = new Configuration() + this.LocalConfiguration = new Configuration { FileSystem = this.fileSystem.Object }; this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object); this.LocalConfiguration.SetDecoder(localImageFormatMock.Object, this.localDecoder.Object); - TestFormat.RegisterGloablTestFormat(); + TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); this.DataStream = TestFormat.GlobalTestFormat.CreateStream(this.Marker); this.FilePath = Guid.NewGuid().ToString(); this.fileSystem.Setup(x => x.OpenRead(this.FilePath)).Returns(this.DataStream); - TestFileSystem.RegisterGloablTestFormat(); + TestFileSystem.RegisterGlobalTestFormat(); TestFileSystem.Global.AddFile(this.FilePath, this.DataStream); } [Fact] public void LoadFromStream() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); @@ -87,12 +85,11 @@ namespace ImageSharp.Tests public void LoadFromNoneSeekableStream() { NoneSeekableStream stream = new NoneSeekableStream(this.DataStream); - Image img = Image.Load(stream); + Image img = Image.Load(stream); Assert.NotNull(img); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } [Fact] @@ -104,20 +101,18 @@ namespace ImageSharp.Tests Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } - + [Fact] public void LoadFromStreamWithConfig() { Stream stream = new MemoryStream(); - Image img = Image.Load(this.LocalConfiguration, stream); + Image img = Image.Load(this.LocalConfiguration, stream); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream)); - } [Fact] @@ -130,7 +125,6 @@ namespace ImageSharp.Tests Assert.Equal(this.returnImage, img); this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream)); - } @@ -138,7 +132,7 @@ namespace ImageSharp.Tests public void LoadFromStreamWithDecoder() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.localDecoder.Object); + Image img = Image.Load(stream, this.localDecoder.Object); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream)); @@ -158,13 +152,11 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytes() { - Image img = Image.Load(this.DataStream.ToArray()); + Image img = Image.Load(this.DataStream.ToArray()); Assert.NotNull(img); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } [Fact] @@ -182,7 +174,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithConfig() { - Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); Assert.NotNull(img); @@ -207,7 +199,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithDecoder() { - Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny())); @@ -228,13 +220,11 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFile() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); - TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, Configuration.Default); - } [Fact] @@ -251,12 +241,11 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithConfig() { - Image img = Image.Load(this.LocalConfiguration, this.FilePath); + Image img = Image.Load(this.LocalConfiguration, this.FilePath); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream)); - } [Fact] @@ -273,7 +262,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithDecoder() { - Image img = Image.Load(this.FilePath, this.localDecoder.Object); + Image img = Image.Load(this.FilePath, this.localDecoder.Object); Assert.NotNull(img); this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream)); @@ -305,7 +294,6 @@ namespace ImageSharp.Tests Assert.Equal(Rgba32.White, px[1, 0]); Assert.Equal(Rgba32.Black, px[1, 1]); - } } @@ -327,7 +315,18 @@ namespace ImageSharp.Tests Assert.Equal(Rgba32.White, px[1, 0]); Assert.Equal(Rgba32.Black, px[1, 1]); + } + } + + [Fact] + public void LoadsImageWithoutThrowingCrcException() + { + var image1Provider = TestImageProvider.File(TestImages.Png.VersioningImage1); + + using (Image img = image1Provider.GetImage()) + { + Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length); } } diff --git a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs new file mode 100644 index 0000000000..73c2f78f40 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Helpers; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageRotationTests + { + [Fact] + public void RotateImageByMinus90Degrees() + { + (Size original, Size rotated) = Rotate(-90); + Assert.Equal(new Size(original.Height, original.Width), rotated); + } + + [Fact] + public void RotateImageBy90Degrees() + { + (Size original, Size rotated) = Rotate(90); + Assert.Equal(new Size(original.Height, original.Width), rotated); + } + + [Fact] + public void RotateImageBy180Degrees() + { + (Size original, Size rotated) = Rotate(180); + Assert.Equal(original, rotated); + } + + [Fact] + public void RotateImageBy270Degrees() + { + (Size original, Size rotated) = Rotate(270); + Assert.Equal(new Size(original.Height, original.Width), rotated); + } + + [Fact] + public void RotateImageBy360Degrees() + { + (Size original, Size rotated) = Rotate(360); + Assert.Equal(original, rotated); + } + + private static (Size original, Size rotated) Rotate(int angle) + { + var file = TestFile.Create(TestImages.Bmp.Car); + using (var image = Image.Load(file.FullPath)) + { + Size original = image.Size(); + image.Mutate(x => x.Rotate(angle)); + return (original, image.Size()); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index eecb983800..5b672059c2 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -1,19 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.IO; - using System.Linq; - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - - using Moq; - using Xunit; + using System.Runtime.CompilerServices; /// /// Tests the class. @@ -50,6 +50,56 @@ namespace ImageSharp.Tests this.Image = new Image(config, 1, 1); } + [Theory] + [WithTestPatternImages(13, 19, PixelTypes.Rgba32 | PixelTypes.Bgr24)] + public void SavePixelData_ToPixelStructArray(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + TPixel[] buffer = new TPixel[image.Width*image.Height]; + image.SavePixelData(buffer); + + image.ComparePixelBufferTo(buffer); + + // TODO: We need a separate test-case somewhere ensuring that image pixels are stored in row-major order! + } + } + + [Theory] + [WithTestPatternImages(19, 13, PixelTypes.Rgba32 | PixelTypes.Bgr24)] + public void SavePixelData_ToByteArray(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + byte[] buffer = new byte[image.Width*image.Height*Unsafe.SizeOf()]; + + image.SavePixelData(buffer); + + image.ComparePixelBufferTo(buffer.AsSpan().NonPortableCast()); + } + } + + [Fact] + public void SavePixelData_Rgba32_WhenBufferIsTooSmall_Throws() + { + using (var img = new Image(2, 2)) + { + img[0, 0] = Rgba32.White; + img[1, 0] = Rgba32.Black; + + img[0, 1] = Rgba32.Red; + img[1, 1] = Rgba32.Blue; + var buffer = new byte[2 * 2]; // width * height * bytes per pixel + + Assert.Throws(() => + { + img.SavePixelData(buffer); + }); + } + } + [Fact] public void SavePath() { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 3157c27d8d..da813f4280 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -1,21 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// - public class ImageTests + public class ImageTests : FileTestBase { [Fact] public void ConstructorByteArray() @@ -37,7 +35,7 @@ namespace ImageSharp.Tests public void ConstructorFileSystem() { TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = Image.Load(file.FilePath)) + using (Image image = Image.Load(file.FullPath)) { Assert.Equal(600, image.Width); Assert.Equal(450, image.Height); @@ -67,8 +65,9 @@ namespace ImageSharp.Tests [Fact] public void Save_DetecedEncoding() { - string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.png"); - System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); + string file = System.IO.Path.Combine(dir, "Save_DetecedEncoding.png"); + using (Image image = new Image(10, 10)) { image.Save(file); @@ -81,9 +80,11 @@ namespace ImageSharp.Tests } [Fact] - public void Save_UnknownExtensionsEncoding() + public void Save_WhenExtensionIsUnknown_Throws() { - string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.tmp"); + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); + string file = System.IO.Path.Combine(dir, "Save_UnknownExtensionsEncoding_Throws.tmp"); + NotSupportedException ex = Assert.Throws( () => { @@ -97,8 +98,9 @@ namespace ImageSharp.Tests [Fact] public void Save_SetEncoding() { - string file = TestFile.GetPath("../../TestOutput/Save_SetEncoding.dat"); - System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); + string file = System.IO.Path.Combine(dir, "Save_SetEncoding.dat"); + using (Image image = new Image(10, 10)) { image.Save(file, new PngEncoder()); diff --git a/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs b/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs index bc36b60eb2..10a531eafe 100644 --- a/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs +++ b/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs @@ -1,7 +1,10 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.IO; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal class NoneSeekableStream : Stream { diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index b59023340b..1ab3f2ce9f 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -1,36 +1,33 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// public class PixelAccessorTests { - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + var image = new Image(10, 10); using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { - Vector4 v = new Vector4(i, j, 0, 1); + var v = new Vector4(i, j, 0, 1); v /= 10; - TPixel color = default(TPixel); + var color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -45,7 +42,7 @@ namespace ImageSharp.Tests [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyx)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) + internal void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) where TPixel : struct, IPixel { using (Image src = provider.GetImage()) @@ -75,14 +72,14 @@ namespace ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) + internal void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) where TPixel : struct, IPixel { using (Image destImage = new Image(8, 8)) { using (Image srcImage = provider.GetImage()) { - srcImage.Fill(NamedColors.Red, new Rectangle(4, 4, 8, 8)); + srcImage.Mutate(x => x.Fill(NamedColors.Red, new Rectangle(4, 4, 8, 8))); using (PixelAccessor srcPixels = srcImage.Lock()) { using (PixelArea area = new PixelArea(8, 8, order)) @@ -100,8 +97,9 @@ namespace ImageSharp.Tests provider.Utility.SourceFileOrDescription = order.ToString(); provider.Utility.SaveTestOutputFile(destImage, "bmp"); - using (Image expectedImage = new Image(8, 8).Fill(NamedColors.Red)) + using (Image expectedImage = new Image(8, 8)) { + expectedImage.Mutate(x => x.Fill(NamedColors.Red)); Assert.True(destImage.IsEquivalentTo(expectedImage)); } } @@ -113,7 +111,7 @@ namespace ImageSharp.Tests { using (Image image = new Image(1, 1)) { - CopyFromZYX(image); + CopyFromZYXImpl(image); } } @@ -122,7 +120,7 @@ namespace ImageSharp.Tests { using (Image image = new Image(1, 1)) { - CopyFromZYXW(image); + CopyFromZYXWImpl(image); } } @@ -131,7 +129,7 @@ namespace ImageSharp.Tests { using (Image image = new Image(1, 1)) { - CopyToZYX(image); + CopyToZYXImpl(image); } } @@ -140,11 +138,11 @@ namespace ImageSharp.Tests { using (Image image = new Image(1, 1)) { - CopyToZYXW(image); + CopyToZYXWImpl(image); } } - private static void CopyFromZYX(Image image) + private static void CopyFromZYXImpl(Image image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) @@ -171,7 +169,7 @@ namespace ImageSharp.Tests } } - private static void CopyFromZYXW(Image image) + private static void CopyFromZYXWImpl(Image image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) @@ -199,7 +197,7 @@ namespace ImageSharp.Tests } } - private static void CopyToZYX(Image image) + private static void CopyToZYXImpl(Image image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) @@ -221,7 +219,7 @@ namespace ImageSharp.Tests } } - private static void CopyToZYXW(Image image) + private static void CopyToZYXWImpl(Image image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs deleted file mode 100644 index 6cd80e9e83..0000000000 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ /dev/null @@ -1,160 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System; - using ImageSharp; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - /// - /// Class to perform simple image comparisons. - /// - public static class ImageComparer - { - const int DefaultScalingFactor = 32; // This is means the images get scaled into a 32x32 image to sample pixels - const int DefaultSegmentThreshold = 3; // The greyscale difference between 2 segements my be > 3 before it influences the overall difference - const float DefaultImageThreshold = 0.000F; // After segment thresholds the images must have no differences - - /// - /// Fills the bounded area with a solid color and does a visual comparison between 2 images asserting the difference outwith - /// that area is less then a configurable threshold. - /// - /// The color of the expected image - /// The color type fo the the actual image - /// The expected image - /// The actual image - /// The bounds within the image has been altered - /// - /// The threshold for the percentage difference where the images are asumed to be the same. - /// The default/undefined value is - /// - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// - public static void EnsureProcessorChangesAreConstrained(Image expected, Image actual, Rectangle bounds, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - // Draw identical shapes over the bounded and compare to ensure changes are constrained. - expected.Fill(NamedColors.HotPink, bounds); - actual.Fill(NamedColors.HotPink, bounds); - - CheckSimilarity(expected, actual, imageTheshold, segmentThreshold, scalingFactor); - } - - /// - /// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold - /// - /// The color of the expected image - /// The color type fo the the actual image - /// The expected image - /// The actual image - /// - /// The threshold for the percentage difference where the images are asumed to be the same. - /// The default/undefined value is - /// - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// - public static void CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - float percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); - - Assert.InRange(percentage, 0, imageTheshold); - } - - /// - /// Does a visual comparison between 2 images and then and returns the percentage diffence between the 2 - /// - /// The color of the source image - /// The color type for the target image - /// The source image - /// The target image - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// - /// Returns a number from 0 - 1 which represents the difference focter between the images. - public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - // code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET - Fast2DArray differences = GetDifferences(source, target, scalingFactor); - - int diffPixels = 0; - - foreach (byte b in differences.Data) - { - if (b > segmentThreshold) { diffPixels++; } - } - - return diffPixels / (float)(scalingFactor * scalingFactor); - } - - private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - var differences = new Fast2DArray(scalingFactor, scalingFactor); - Fast2DArray firstGray = source.GetGrayScaleValues(scalingFactor); - Fast2DArray secondGray = target.GetGrayScaleValues(scalingFactor); - - for (int y = 0; y < scalingFactor; y++) - { - for (int x = 0; x < scalingFactor; x++) - { - int diff = firstGray[x, y] - secondGray[x, y]; - differences[x, y] = (byte)Math.Abs(diff); - } - } - - return differences; - } - - private static Fast2DArray GetGrayScaleValues(this Image source, int scalingFactor) - where TPixelA : struct, IPixel - { - byte[] buffer = new byte[3]; - using (Image img = new Image(source).Resize(scalingFactor, scalingFactor).Grayscale()) - { - using (PixelAccessor pixels = img.Lock()) - { - var grayScale = new Fast2DArray(scalingFactor, scalingFactor); - for (int y = 0; y < scalingFactor; y++) - { - for (int x = 0; x < scalingFactor; x++) - { - pixels[x, y].ToXyzBytes(buffer, 0); - grayScale[x, y] = buffer[0]; - } - } - - return grayScale; - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs new file mode 100644 index 0000000000..59722a84d2 --- /dev/null +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -0,0 +1,104 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Moq; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Tests the configuration class. + /// + public class ImageOperationTests : IDisposable + { + private readonly Image image; + private readonly FakeImageOperationsProvider provider; + private readonly IImageProcessor processor; + + public Configuration Configuration { get; private set; } + + public ImageOperationTests() + { + this.provider = new FakeImageOperationsProvider(); + this.processor = new Mock>().Object; + this.image = new Image(new Configuration() + { + ImageOperationsProvider = this.provider + }, 1, 1); + } + + [Fact] + public void MutateCallsImageOperationsProvider_Func_OriginalImage() + { + this.image.Mutate(x => x.ApplyProcessor(this.processor)); + + Assert.True(this.provider.HasCreated(this.image)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x=>x.Processor)); + } + + [Fact] + public void MutateCallsImageOperationsProvider_ListOfProcessors_OriginalImage() + { + this.image.Mutate(this.processor); + + Assert.True(this.provider.HasCreated(this.image)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_Func_WithDuplicateImage() + { + var returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + + Assert.True(this.provider.HasCreated(returned)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_ListOfProcessors_WithDuplicateImage() + { + var returned = this.image.Clone(this.processor); + + Assert.True(this.provider.HasCreated(returned)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_Func_NotOnOrigional() + { + var returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + Assert.False(this.provider.HasCreated(this.image)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_ListOfProcessors_NotOnOrigional() + { + var returned = this.image.Clone(this.processor); + Assert.False(this.provider.HasCreated(this.image)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + } + + [Fact] + public void ApplyProcessors_ListOfProcessors_AppliesAllProcessorsToOperation() + { + var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); + operations.ApplyProcessors(this.processor); + Assert.Contains(this.processor, operations.applied.Select(x => x.Processor)); + } + + public void Dispose() + { + this.image.Dispose(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 957ac12fea..6dc5d21200 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -5,6 +5,12 @@ full portable True + SixLabors.ImageSharp.Tests + SixLabors.ImageSharp.Tests + + + true + @@ -12,14 +18,21 @@ - - - - - + + + + + + + + + + @@ -31,4 +44,7 @@ PreserveNewest + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject b/tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject new file mode 100644 index 0000000000..f015b4b86e --- /dev/null +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject @@ -0,0 +1,9 @@ + + + False + UseStaticAnalysis + + False + False + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Issues/Issue412.cs b/tests/ImageSharp.Tests/Issues/Issue412.cs new file mode 100644 index 0000000000..b77112ba68 --- /dev/null +++ b/tests/ImageSharp.Tests/Issues/Issue412.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.Primitives; +using SixLabors.ImageSharp.Advanced; +using Xunit; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.Issues +{ + public class Issue412 + { + [Theory] + [WithBlankImages(40, 30, PixelTypes.Rgba32)] + public void AllPixelsExpectedToBeRedWhenAntialisedDisabled(TestImageProvider provider) where TPixel : struct, IPixel + { + using (var image = provider.GetImage()) + { + image.Mutate( + context => + { + for (var i = 0; i < 40; ++i) + { + context.DrawLines( + NamedColors.Black, + 1, + new[] + { + new PointF(i, 0.1066f), + new PointF(i, 10.1066f) + }, + new GraphicsOptions(true)); + + context.DrawLines( + NamedColors.Red, + 1, + new[] + { + new PointF(i, 15.1066f), + new PointF(i, 25.1066f) + }, + new GraphicsOptions(false)); + } + }); + + image.DebugSave(provider); + for (var y = 15; y < 25; y++) + { + for (var x = 0; x < 40; x++) + { + + Assert.True(NamedColors.Red.Equals(image[x, y]), $"expected {NamedColors.Red} but found {image[x, y]} at [{x}, {y}]"); + } + } + } + } + } +} diff --git a/tests/ImageSharp.Tests/Common/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs similarity index 71% rename from tests/ImageSharp.Tests/Common/Buffer2DTests.cs rename to tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 5f44a132d7..d662a1b3ef 100644 --- a/tests/ImageSharp.Tests/Common/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -1,16 +1,17 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Common +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Runtime.CompilerServices; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Tests.Common; using Xunit; - using static TestStructs; - - public unsafe class Buffer2DTests + public class Buffer2DTests { // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert @@ -30,7 +31,7 @@ namespace ImageSharp.Tests.Common [InlineData(1025, 17)] public void Construct(int width, int height) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -43,8 +44,8 @@ namespace ImageSharp.Tests.Common [InlineData(1025, 17)] public void Construct_FromExternalArray(int width, int height) { - Foo[] array = new Foo[width * height + 10]; - using (Buffer2D buffer = new Buffer2D(array, width, height)) + TestStructs.Foo[] array = new TestStructs.Foo[width * height + 10]; + using (Buffer2D buffer = new Buffer2D(array, width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -75,9 +76,9 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - Span span = buffer.GetRowSpan(y); + Span span = buffer.GetRowSpan(y); // Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); @@ -91,9 +92,9 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - Span span = buffer.GetRowSpan(x, y); + Span span = buffer.GetRowSpan(x, y); // Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); @@ -107,13 +108,13 @@ namespace ImageSharp.Tests.Common [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - Foo[] array = buffer.Array; + TestStructs.Foo[] array = buffer.Array; - ref Foo actual = ref buffer[x, y]; + ref TestStructs.Foo actual = ref buffer[x, y]; - ref Foo expected = ref array[y * width + x]; + ref TestStructs.Foo expected = ref array[y * width + x]; Assert.True(Unsafe.AreSame(ref expected, ref actual)); } diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs new file mode 100644 index 0000000000..026b694981 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -0,0 +1,114 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Memory +{ + using System; + + using SixLabors.ImageSharp.Memory; + using SixLabors.Primitives; + + using Xunit; + + public class BufferAreaTests + { + [Fact] + public void Construct() + { + using (var buffer = new Buffer2D(10, 20)) + { + var rectangle = new Rectangle(3,2, 5, 6); + var area = new BufferArea(buffer, rectangle); + + Assert.Equal(buffer, area.DestinationBuffer); + Assert.Equal(rectangle, area.Rectangle); + } + } + + private static Buffer2D CreateTestBuffer(int w, int h) + { + var buffer = new Buffer2D(w, h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + buffer[x, y] = y * 100 + x; + } + } + return buffer; + } + + [Theory] + [InlineData(2, 3, 2, 2)] + [InlineData(5, 4, 3, 2)] + public void Indexer(int rx, int ry, int x, int y) + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + Rectangle r = new Rectangle(rx, ry, 5, 6); + + BufferArea area = buffer.GetArea(r); + + int value = area[x, y]; + int expected = (ry + y) * 100 + rx + x; + Assert.Equal(expected, value); + } + } + + [Theory] + [InlineData(2, 3, 2, 5, 6)] + [InlineData(5, 4, 3, 6, 5)] + public void GetRowSpan(int rx, int ry, int y, int w, int h) + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + Rectangle r = new Rectangle(rx, ry, w, h); + + BufferArea area = buffer.GetArea(r); + + Span span = area.GetRowSpan(y); + + Assert.Equal(w, span.Length); + + for (int i = 0; i < w; i++) + { + int expected = (ry + y) * 100 + rx + i; + int value = span[i]; + + Assert.Equal(expected, value); + } + } + } + + [Fact] + public void GetSubArea() + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + BufferArea area0 = buffer.GetArea(6, 8, 10, 10); + + BufferArea area1 = area0.GetSubArea(4, 4, 5, 5); + + var expectedRect = new Rectangle(10, 12, 5, 5); + + Assert.Equal(buffer, area1.DestinationBuffer); + Assert.Equal(expectedRect, area1.Rectangle); + + int value00 = 12 * 100 + 10; + Assert.Equal(value00, area1[0, 0]); + } + } + + [Fact] + public void DangerousGetPinnableReference() + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + BufferArea area0 = buffer.GetArea(6, 8, 10, 10); + + ref int r = ref area0.GetReferenceToOrigo(); + + int expected = buffer[6, 8]; + Assert.Equal(expected, r); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/BufferTests.cs b/tests/ImageSharp.Tests/Memory/BufferTests.cs similarity index 69% rename from tests/ImageSharp.Tests/Common/BufferTests.cs rename to tests/ImageSharp.Tests/Memory/BufferTests.cs index 25ef173d4c..e1efeb24e8 100644 --- a/tests/ImageSharp.Tests/Common/BufferTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTests.cs @@ -1,17 +1,15 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Common +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Threading.Tasks; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; using Xunit; - using static TestStructs; - public unsafe class BufferTests { // ReSharper disable once ClassNeverInstantiated.Local @@ -37,7 +35,7 @@ namespace ImageSharp.Tests.Common [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (Buffer buffer = new Buffer(count)) + using (Buffer buffer = new Buffer(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); @@ -51,8 +49,8 @@ namespace ImageSharp.Tests.Common [InlineData(1111)] public void ConstructWithExistingArray(int count) { - Foo[] array = new Foo[count]; - using (Buffer buffer = new Buffer(array)) + TestStructs.Foo[] array = new TestStructs.Foo[count]; + using (Buffer buffer = new Buffer(array)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); @@ -63,13 +61,13 @@ namespace ImageSharp.Tests.Common [Fact] public void Clear() { - Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; - using (Buffer buffer = new Buffer(a)) + TestStructs.Foo[] a = { new TestStructs.Foo() { A = 1, B = 2 }, new TestStructs.Foo() { A = 3, B = 4 } }; + using (Buffer buffer = new Buffer(a)) { buffer.Clear(); - Assert.Equal(default(Foo), a[0]); - Assert.Equal(default(Foo), a[1]); + Assert.Equal(default(TestStructs.Foo), a[0]); + Assert.Equal(default(TestStructs.Foo), a[1]); } } @@ -103,11 +101,11 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Read(int length, int index) { - Foo[] a = Foo.CreateArray(length); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - using (Buffer buffer = new Buffer(a)) + using (Buffer buffer = new Buffer(a)) { - Foo element = buffer[index]; + TestStructs.Foo element = buffer[index]; Assert.Equal(a[index], element); } @@ -117,13 +115,13 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Write(int length, int index) { - Foo[] a = Foo.CreateArray(length); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - using (Buffer buffer = new Buffer(a)) + using (Buffer buffer = new Buffer(a)) { - buffer[index] = new Foo(666, 666); + buffer[index] = new TestStructs.Foo(666, 666); - Assert.Equal(new Foo(666, 666), a[index]); + Assert.Equal(new TestStructs.Foo(666, 666), a[index]); } } } @@ -131,7 +129,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Dispose() { - Buffer buffer = new Buffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -142,9 +140,9 @@ namespace ImageSharp.Tests.Common [InlineData(123)] public void CastToSpan(int bufferLength) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - Span span = buffer; + Span span = buffer; //Assert.Equal(buffer.Array, span.ToArray()); //Assert.Equal(0, span.Start); @@ -156,9 +154,9 @@ namespace ImageSharp.Tests.Common [Fact] public void Span() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = new Buffer(42)) { - Span span = buffer.Span; + Span span = buffer.Span; // Assert.Equal(buffer.Array, span.ToArray()); // Assert.Equal(0, span.Start); @@ -175,9 +173,9 @@ namespace ImageSharp.Tests.Common [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - Span span = buffer.Slice(start); + Span span = buffer.Slice(start); Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, bufferLength - start); @@ -189,9 +187,9 @@ namespace ImageSharp.Tests.Common [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - Span span = buffer.Slice(start, spanLength); + Span span = buffer.Slice(start, spanLength); Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, spanLength); @@ -202,8 +200,8 @@ namespace ImageSharp.Tests.Common [Fact] public void UnPinAndTakeArrayOwnership() { - Foo[] data = null; - using (Buffer buffer = new Buffer(42)) + TestStructs.Foo[] data = null; + using (Buffer buffer = new Buffer(42)) { data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -218,10 +216,10 @@ namespace ImageSharp.Tests.Common [Fact] public void ReturnsPinnedPointerToTheBeginningOfArray() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = new Buffer(42)) { - Foo* actual = (Foo*)buffer.Pin(); - fixed (Foo* expected = buffer.Array) + TestStructs.Foo* actual = (TestStructs.Foo*)buffer.Pin(); + fixed (TestStructs.Foo* expected = buffer.Array) { Assert.Equal(expected, actual); } @@ -231,7 +229,7 @@ namespace ImageSharp.Tests.Common [Fact] public void SecondCallReturnsTheSamePointer() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = new Buffer(42)) { IntPtr ptr1 = buffer.Pin(); IntPtr ptr2 = buffer.Pin(); @@ -243,7 +241,7 @@ namespace ImageSharp.Tests.Common [Fact] public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() { - Buffer buffer = new Buffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.Throws(() => buffer.Pin()); diff --git a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs similarity index 91% rename from tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs rename to tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs index efdcaa8484..5cdbe638a6 100644 --- a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs +++ b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Common +namespace SixLabors.ImageSharp.Tests.Memory { using System; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs b/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs new file mode 100644 index 0000000000..caba9a4647 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Linq; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Memory +{ + using System; + + /// + /// Tests the class. + /// + public class PixelDataPoolTests + { + private const int MaxPooledBufferSizeInBytes = PixelDataPool.MaxPooledBufferSizeInBytes; + + readonly object monitor = new object(); + + [Theory] + [InlineData(1)] + [InlineData(1024)] + public void PixelDataPoolRentsMinimumSize(int size) + { + Rgba32[] pixels = PixelDataPool.Rent(size); + + Assert.True(pixels.Length >= size); + } + + [Fact] + public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() + { + Rgba32[] pixels = new Rgba32[1024]; + + PixelDataPool.Return(pixels); + + Assert.True(pixels.Length >= 1024); + } + + /// + /// Rent 'n' buffers -> return all -> re-rent, verify if there is at least one in common. + /// + private bool CheckIsPooled(int n, int count) + where T : struct + { + lock (this.monitor) + { + T[][] original = new T[n][]; + + for (int i = 0; i < n; i++) + { + original[i] = PixelDataPool.Rent(count); + } + + for (int i = 0; i < n; i++) + { + PixelDataPool.Return(original[i]); + } + + T[][] verification = new T[n][]; + + for (int i = 0; i < n; i++) + { + verification[i] = PixelDataPool.Rent(count); + } + + return original.Intersect(verification).Any(); + } + } + + [Theory] + [InlineData(32)] + [InlineData(512)] + [InlineData(MaxPooledBufferSizeInBytes-1)] + public void SmallBuffersArePooled(int size) + { + Assert.True(this.CheckIsPooled(5, size)); + } + + [Theory] + [InlineData(128 * 1024 * 1024)] + [InlineData(MaxPooledBufferSizeInBytes+1)] + public void LargeBuffersAreNotPooled_OfByte(int size) + { + Assert.False(this.CheckIsPooled(2, size)); + } + + [StructLayout(LayoutKind.Explicit, Size = 512)] + struct TestStruct + { + } + + [Fact] + public unsafe void LaregeBuffersAreNotPooled_OfBigValueType() + { + const int mb128 = 128 * 1024 * 1024; + int count = mb128 / sizeof(TestStruct); + + Assert.False(this.CheckIsPooled(2, count)); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs similarity index 63% rename from tests/ImageSharp.Tests/Common/BufferSpanTests.cs rename to tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index af33a981ba..395c325461 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -1,20 +1,18 @@ -// ReSharper disable ObjectCreationAsStatement -// ReSharper disable InconsistentNaming +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. -namespace ImageSharp.Tests.Common +namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Numerics; using System.Runtime.CompilerServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Tests.Common; using Xunit; - using static TestStructs; - - public unsafe class SpanTests + public unsafe class SpanUtilityTests { // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert @@ -23,7 +21,7 @@ namespace ImageSharp.Tests.Common { ref T1 bb = ref Unsafe.As(ref b); - True(Unsafe.AreSame(ref a, ref bb), "References are not same!"); + Assert.True(Unsafe.AreSame(ref a, ref bb), "References are not same!"); } } @@ -45,15 +43,15 @@ namespace ImageSharp.Tests.Common [Fact] public void AsBytes() { - Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) }; + TestStructs.Foo[] fooz = { new TestStructs.Foo(1, 2), new TestStructs.Foo(3, 4), new TestStructs.Foo(5, 6) }; - using (Buffer colorBuf = new Buffer(fooz)) + using (Buffer colorBuf = new Buffer(fooz)) { - Span orig = colorBuf.Slice(1); + Span orig = colorBuf.Slice(1); Span asBytes = orig.AsBytes(); // Assert.Equal(asBytes.Start, sizeof(Foo)); - Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); + Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference()); } } @@ -63,10 +61,10 @@ namespace ImageSharp.Tests.Common [Fact] public void Basic() { - Foo[] array = Foo.CreateArray(3); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(3); // Act: - Span span = new Span(array); + Span span = new Span(array); // Assert: Assert.Equal(array, span.ToArray()); @@ -77,11 +75,11 @@ namespace ImageSharp.Tests.Common [Fact] public void WithStart() { - Foo[] array = Foo.CreateArray(4); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(4); int start = 2; // Act: - Span span = new Span(array, start); + Span span = new Span(array, start); // Assert: Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); @@ -91,11 +89,11 @@ namespace ImageSharp.Tests.Common [Fact] public void WithStartAndLength() { - Foo[] array = Foo.CreateArray(10); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(10); int start = 2; int length = 3; // Act: - Span span = new Span(array, start, length); + Span span = new Span(array, start, length); // Assert: Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); @@ -108,12 +106,12 @@ namespace ImageSharp.Tests.Common [Fact] public void StartOnly() { - Foo[] array = Foo.CreateArray(5); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(5); int start0 = 2; int start1 = 2; int totalOffset = start0 + start1; - Span span = new Span(array, start0); + Span span = new Span(array, start0); // Act: span = span.Slice(start1); @@ -126,13 +124,13 @@ namespace ImageSharp.Tests.Common [Fact] public void StartAndLength() { - Foo[] array = Foo.CreateArray(10); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(10); int start0 = 2; int start1 = 2; int totalOffset = start0 + start1; int sliceLength = 3; - Span span = new Span(array, start0); + Span span = new Span(array, start0); // Act: span = span.Slice(start1, sliceLength); @@ -179,10 +177,10 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Read(int length, int start, int index) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); - Foo element = span[index]; + TestStructs.Foo element = span[index]; Assert.Equal(a[start + index], element); } @@ -191,12 +189,12 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Write(int length, int start, int index) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); - span[index] = new Foo(666, 666); + span[index] = new TestStructs.Foo(666, 666); - Assert.Equal(new Foo(666, 666), a[start + index]); + Assert.Equal(new TestStructs.Foo(666, 666), a[start + index]); } [Theory] @@ -206,15 +204,15 @@ namespace ImageSharp.Tests.Common [InlineData(10, 1, 1, 7)] public void AsBytes_Read(int length, int start, int index, int byteOffset) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); Span bytes = span.AsBytes(); - byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; + byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; - ref byte baseRef = ref Unsafe.As(ref a[0]); - byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); + ref byte baseRef = ref Unsafe.As(ref a[0]); + byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); Assert.Equal(expected, actual); } @@ -226,9 +224,9 @@ namespace ImageSharp.Tests.Common [InlineData(3, 4)] public void DangerousGetPinnableReference(int start, int length) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); - ref Foo r = ref span.DangerousGetPinnableReference(); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); + ref TestStructs.Foo r = ref span.DangerousGetPinnableReference(); Assert.True(Unsafe.AreSame(ref a[start], ref r)); } @@ -266,11 +264,11 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToOwnType(int count) { - Foo[] source = Foo.CreateArray(count + 2); - Foo[] dest = new Foo[count + 5]; + TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); + TestStructs.Foo[] dest = new TestStructs.Foo[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -289,11 +287,11 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToOwnType_Aligned(int count) { - AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); - AlignedFoo[] dest = new AlignedFoo[count + 5]; + TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); + TestStructs.AlignedFoo[] dest = new TestStructs.AlignedFoo[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -335,22 +333,22 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToBytes(int count) { - int destCount = count * sizeof(Foo); - Foo[] source = Foo.CreateArray(count + 2); - byte[] dest = new byte[destCount + sizeof(Foo) * 2]; + int destCount = count * sizeof(TestStructs.Foo); + TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(TestStructs.Foo) * 2]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, sizeof(Foo)); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, sizeof(TestStructs.Foo)); - SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(Foo)); + SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.Foo)); AssertNotDefault(source, 1); - Assert.False(ElementsAreEqual(source, dest, 0)); - Assert.True(ElementsAreEqual(source, dest, 1)); - Assert.True(ElementsAreEqual(source, dest, 2)); - Assert.True(ElementsAreEqual(source, dest, count - 1)); - Assert.False(ElementsAreEqual(source, dest, count)); + Assert.False((bool)ElementsAreEqual(source, dest, 0)); + Assert.True((bool)ElementsAreEqual(source, dest, 1)); + Assert.True((bool)ElementsAreEqual(source, dest, 2)); + Assert.True((bool)ElementsAreEqual(source, dest, count - 1)); + Assert.False((bool)ElementsAreEqual(source, dest, count)); } [Theory] @@ -358,22 +356,22 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToBytes_Aligned(int count) { - int destCount = count * sizeof(Foo); - AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); - byte[] dest = new byte[destCount + sizeof(AlignedFoo) * 2]; + int destCount = count * sizeof(TestStructs.Foo); + TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(TestStructs.AlignedFoo) * 2]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, sizeof(AlignedFoo)); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, sizeof(TestStructs.AlignedFoo)); - SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(AlignedFoo)); + SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.AlignedFoo)); AssertNotDefault(source, 1); - Assert.False(ElementsAreEqual(source, dest, 0)); - Assert.True(ElementsAreEqual(source, dest, 1)); - Assert.True(ElementsAreEqual(source, dest, 2)); - Assert.True(ElementsAreEqual(source, dest, count - 1)); - Assert.False(ElementsAreEqual(source, dest, count)); + Assert.False((bool)ElementsAreEqual(source, dest, 0)); + Assert.True((bool)ElementsAreEqual(source, dest, 1)); + Assert.True((bool)ElementsAreEqual(source, dest, 2)); + Assert.True((bool)ElementsAreEqual(source, dest, count - 1)); + Assert.False((bool)ElementsAreEqual(source, dest, count)); } [Theory] @@ -392,9 +390,9 @@ namespace ImageSharp.Tests.Common AssertNotDefault(source, 1); - Assert.True(ElementsAreEqual(source, dest, 0)); - Assert.True(ElementsAreEqual(source, dest, count - 1)); - Assert.False(ElementsAreEqual(source, dest, count)); + Assert.True((bool)ElementsAreEqual(source, dest, 0)); + Assert.True((bool)ElementsAreEqual(source, dest, count - 1)); + Assert.False((bool)ElementsAreEqual(source, dest, count)); } [Theory] @@ -402,22 +400,22 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void BytesToGeneric(int count) { - int srcCount = count * sizeof(Foo); + int srcCount = count * sizeof(TestStructs.Foo); byte[] source = CreateTestBytes(srcCount); - Foo[] dest = new Foo[count + 2]; + TestStructs.Foo[] dest = new TestStructs.Foo[count + 2]; Span apSource = new Span(source); - Span apDest = new Span(dest); + Span apDest = new Span(dest); - SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(Foo)); + SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(TestStructs.Foo)); - AssertNotDefault(source, sizeof(Foo) + 1); + AssertNotDefault(source, sizeof(TestStructs.Foo) + 1); AssertNotDefault(dest, 1); - Assert.True(ElementsAreEqual(dest, source, 0)); - Assert.True(ElementsAreEqual(dest, source, 1)); - Assert.True(ElementsAreEqual(dest, source, count - 1)); - Assert.False(ElementsAreEqual(dest, source, count)); + Assert.True((bool)ElementsAreEqual(dest, source, 0)); + Assert.True((bool)ElementsAreEqual(dest, source, 1)); + Assert.True((bool)ElementsAreEqual(dest, source, count - 1)); + Assert.False((bool)ElementsAreEqual(dest, source, count)); } [Fact] @@ -439,29 +437,29 @@ namespace ImageSharp.Tests.Common } } - internal static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) + internal static bool ElementsAreEqual(TestStructs.Foo[] array, byte[] rawArray, int index) { - fixed (Foo* pArray = array) + fixed (TestStructs.Foo* pArray = array) fixed (byte* pRaw = rawArray) { - Foo* pCasted = (Foo*)pRaw; + TestStructs.Foo* pCasted = (TestStructs.Foo*)pRaw; - Foo val1 = pArray[index]; - Foo val2 = pCasted[index]; + TestStructs.Foo val1 = pArray[index]; + TestStructs.Foo val2 = pCasted[index]; return val1.Equals(val2); } } - internal static bool ElementsAreEqual(AlignedFoo[] array, byte[] rawArray, int index) + internal static bool ElementsAreEqual(TestStructs.AlignedFoo[] array, byte[] rawArray, int index) { - fixed (AlignedFoo* pArray = array) + fixed (TestStructs.AlignedFoo* pArray = array) fixed (byte* pRaw = rawArray) { - AlignedFoo* pCasted = (AlignedFoo*)pRaw; + TestStructs.AlignedFoo* pCasted = (TestStructs.AlignedFoo*)pRaw; - AlignedFoo val1 = pArray[index]; - AlignedFoo val2 = pCasted[index]; + TestStructs.AlignedFoo val1 = pArray[index]; + TestStructs.AlignedFoo val2 = pCasted[index]; return val1.Equals(val2); } diff --git a/tests/ImageSharp.Tests/Common/TestStructs.cs b/tests/ImageSharp.Tests/Memory/TestStructs.cs similarity index 91% rename from tests/ImageSharp.Tests/Common/TestStructs.cs rename to tests/ImageSharp.Tests/Memory/TestStructs.cs index f7f09bea20..608e3c6cb3 100644 --- a/tests/ImageSharp.Tests/Common/TestStructs.cs +++ b/tests/ImageSharp.Tests/Memory/TestStructs.cs @@ -1,4 +1,7 @@ -namespace ImageSharp.Tests.Common +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Memory { using Xunit; diff --git a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs index da2bb41561..507401398e 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs @@ -1,13 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.Formats; - using Xunit; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index c60c8978ce..1cb35596c5 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -1,15 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// @@ -24,22 +24,18 @@ namespace ImageSharp.Tests ImageProperty imageProperty = new ImageProperty("name", "value"); metaData.ExifProfile = exifProfile; - metaData.FrameDelay = 42; metaData.HorizontalResolution = 4; metaData.VerticalResolution = 2; metaData.Properties.Add(imageProperty); metaData.RepeatCount = 1; - metaData.DisposalMethod = DisposalMethod.RestoreToBackground; ImageMetaData clone = new ImageMetaData(metaData); Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray()); - Assert.Equal(42, clone.FrameDelay); Assert.Equal(4, clone.HorizontalResolution); Assert.Equal(2, clone.VerticalResolution); Assert.Equal(imageProperty, clone.Properties[0]); Assert.Equal(1, clone.RepeatCount); - Assert.Equal(DisposalMethod.RestoreToBackground, clone.DisposalMethod); } [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs index 3b224014da..5e15d556c0 100644 --- a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index db22300fa5..edeeebd28e 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections; - using System.IO; - using System.Linq; - using System.Text; - - using ImageSharp.PixelFormats; - - using Xunit; - public class ExifProfileTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs index dc62f1cbf3..dee6d5ff39 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Collections.ObjectModel; - using Xunit; +using System.Collections.ObjectModel; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ExifReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 1d36de5ef3..2b8d4d7160 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ExifDescriptionAttributeTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index 473af77121..8bc192af5c 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Linq; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Linq; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ExifValueTests { private static ExifValue GetExifValue() diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs index 2bde125435..beca4db49f 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderCurvesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs index 52c67ba539..43a1ed7f47 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderLutTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs index 9d148ec94a..328cc3fa6a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderMatrixTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs index c02ef40e37..5599e80d19 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderMultiProcessElementTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs index e3593bfa9e..880fa0607e 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using System.Numerics; - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderNonPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs index b9b0c6655c..f8924c43cc 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs index 76bb1cda19..aba587846a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderTagDataEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs index 635ce6168d..a3e5a20f68 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs index c04401f6d0..6a47c988cd 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterCurvesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs index 4fcc55d016..9286ac815e 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterLutTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs index 61b5d57ffd..437c223fa9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System.Numerics; - - using ImageSharp.Memory; - - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterMatrixTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs index e3bc375744..43165c617d 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterMultiProcessElementTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs index ae83458051..eda6a33c74 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using System.Numerics; - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterNonPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs index 8d0cd32ab4..56a4f8c0ca 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs index ea85bd16df..f3ef5effbe 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterTagDataEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs index 5239d7cd51..a3f796275a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs index 34aa24fa66..b3215ee7ae 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs index 7e3f8c0c94..99ae73f49a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccWriterTests { [Fact] diff --git a/tests/ImageSharp.Tests/Numerics/RationalTests.cs b/tests/ImageSharp.Tests/Numerics/RationalTests.cs index 3d80b88fe7..61eeed01f5 100644 --- a/tests/ImageSharp.Tests/Numerics/RationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RationalTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs index cb7e21db0b..af5388d1cf 100644 --- a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 76001ed3a0..ca59cea725 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -1,12 +1,12 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class Bgr24Tests { public static readonly TheoryData ColorData = diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 1928d51f69..e3cf868257 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -1,12 +1,12 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class Bgra32Tests { public static readonly TheoryData ColorData = diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs index eac0644d9a..b0d5929f49 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System.Collections.Generic; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ public class ColorConstructorTests { public static IEnumerable Vector4Data diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs index 7e5af92972..af4181cde2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs @@ -1,21 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ColorDefinitionTests { - public static IEnumerable ColorNames => typeof(NamedColors).GetTypeInfo().GetFields().Select(x => new[] { x.Name }); + public static TheoryData ColorNames + { + get + { + var result = new TheoryData(); + foreach (string name in typeof(NamedColors).GetTypeInfo().GetFields().Select(x => x.Name )) + { + result.Add(name); + } + return result; + } + } [Theory] [MemberData(nameof(ColorNames))] diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs index 2967e7c864..d3815f2eb6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ /// /// Test implementations of IEquatable /// diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs index 563b6be3c4..ad8297fbb5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System.Collections.Generic; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ public class ColorPackingTests { public static IEnumerable Vector4PackData diff --git a/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs index 773bfa513d..9b6d53fd96 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs @@ -1,18 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System; - using System.Diagnostics; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ /// /// The packed pixel tests. /// @@ -38,29 +33,29 @@ namespace ImageSharp.Tests.Colors Assert.Equal(26, new Alpha8(0.1F).PackedValue); // Test ordering - Vector4 vector = new Alpha8(.5F).ToVector4(); + var vector = new Alpha8(.5F).ToVector4(); Assert.Equal(0, vector.X); Assert.Equal(0, vector.Y); Assert.Equal(0, vector.Z); Assert.Equal(.5F, vector.W, 2); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Alpha8(.5F).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 0, 0, 0 }); + new Alpha8(.5F).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(0, 0, 0)); - new Alpha8(.5F).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 0, 0, 0, 128 }); + new Alpha8(.5F).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(0, 0, 0, 128)); - new Alpha8(.5F).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 0, 0 }); + new Alpha8(.5F).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(0, 0, 0)); - new Alpha8(.5F).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 0, 0, 128 }); + new Alpha8(.5F).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(0, 0, 0, 128)); } [Fact] @@ -86,26 +81,26 @@ namespace ImageSharp.Tests.Colors float y = -0.3f; float z = +0.5f; float w = -0.7f; - Argb32 argb = new Argb32(x, y, z, w); + var argb = new Argb32(x, y, z, w); Assert.Equal(0x001a0080u, argb.PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - argb.ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 }); + argb.ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(0x1a, 0, 0x80)); - argb.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 }); + argb.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(0x1a, 0, 0x80, 0)); - argb.ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a }); + argb.ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(0x1a, 0, 0x80)); - argb.ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 }); + argb.ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(0x1a, 0, 0x80, 0)); } [Fact] @@ -137,22 +132,22 @@ namespace ImageSharp.Tests.Colors Assert.Equal(6160, new Bgr565(x, y, z).PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Bgr565(x, y, z).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 25, 0, 132 }); + new Bgr565(x, y, z).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(25, 0, 132)); - new Bgr565(x, y, z).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 25, 0, 132, 255 }); + new Bgr565(x, y, z).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(25, 0, 132, 255)); - new Bgr565(x, y, z).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 132, 0, 25 }); + new Bgr565(x, y, z).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(25, 0, 132)); - new Bgr565(x, y, z).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 132, 0, 25, 255 }); + new Bgr565(x, y, z).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(25, 0, 132, 255)); } [Fact] @@ -187,22 +182,22 @@ namespace ImageSharp.Tests.Colors Assert.Equal(520, new Bgra4444(x, y, z, w).PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Bgra4444(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 34, 0, 136 }); + new Bgra4444(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(34, 0, 136)); - new Bgra4444(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 34, 0, 136, 0 }); + new Bgra4444(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(34, 0, 136, 0)); - new Bgra4444(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 136, 0, 34 }); + new Bgra4444(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(34, 0, 136)); - new Bgra4444(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 136, 0, 34, 0 }); + new Bgra4444(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(34, 0, 136, 0)); } [Fact] @@ -233,22 +228,22 @@ namespace ImageSharp.Tests.Colors Assert.Equal(3088, new Bgra5551(x, y, z, w).PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Bgra5551(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 24, 0, 131 }); + new Bgra5551(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(24, 0, 131)); - new Bgra5551(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 24, 0, 131, 0 }); + new Bgra5551(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(24, 0, 131, 0)); - new Bgra5551(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 131, 0, 24 }); + new Bgra5551(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(24, 0, 131)); - new Bgra5551(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 131, 0, 24, 0 }); + new Bgra5551(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(24, 0, 131, 0)); } [Fact] @@ -284,27 +279,27 @@ namespace ImageSharp.Tests.Colors Assert.Equal((uint)128, new Byte4(x, y, z, w).PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Byte4(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 128, 0, 0 }); + new Byte4(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(128, 0, 0)); - new Byte4(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 128, 0, 0, 0 }); + new Byte4(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(128, 0, 0, 0)); - new Byte4(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 0, 128 }); + new Byte4(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(128, 0, 0)); - new Byte4(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 0, 128, 0 }); + new Byte4(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(128, 0, 0, 0)); - Byte4 r = new Byte4(); + var r = new Byte4(); r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(20, 38, 0, 255)); } [Fact] @@ -323,22 +318,22 @@ namespace ImageSharp.Tests.Colors float x = .5F; Assert.True(Equal(new Vector4(x, 0, 0, 1), new HalfSingle(x).ToVector4())); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new HalfSingle(x).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 128, 0, 0 }); + new HalfSingle(x).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(128, 0, 0)); - new HalfSingle(x).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 128, 0, 0, 255 }); + new HalfSingle(x).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(128, 0, 0, 255)); - new HalfSingle(x).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 0, 128 }); + new HalfSingle(x).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(128, 0, 0)); - new HalfSingle(x).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 0, 128, 255 }); + new HalfSingle(x).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(128, 0, 0, 255)); } [Fact] @@ -360,22 +355,22 @@ namespace ImageSharp.Tests.Colors float y = .25F; Assert.True(Equal(new Vector4(x, y, 0, 1), new HalfVector2(x, y).ToVector4())); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new HalfVector2(x, y).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 128, 64, 0 }); + new HalfVector2(x, y).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(128, 64, 0)); - new HalfVector2(x, y).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 128, 64, 0, 255 }); + new HalfVector2(x, y).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(128, 64, 0, 255)); - new HalfVector2(x, y).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 64, 128 }); + new HalfVector2(x, y).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(128, 64, 0)); - new HalfVector2(x, y).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 64, 128, 255 }); + new HalfVector2(x, y).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(128, 64, 0, 255)); } [Fact] @@ -406,22 +401,22 @@ namespace ImageSharp.Tests.Colors float z = .75F; float w = 1F; - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new HalfVector4(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 64, 128, 191 }); + new HalfVector4(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(64, 128, 191)); - new HalfVector4(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 64, 128, 191, 255 }); + new HalfVector4(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(64, 128, 191, 255)); - new HalfVector4(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 191, 128, 64 }); + new HalfVector4(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(64, 128, 191)); - new HalfVector4(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 191, 128, 64, 255 }); + new HalfVector4(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(64, 128, 191, 255)); } [Fact] @@ -447,26 +442,26 @@ namespace ImageSharp.Tests.Colors float x = 0.1f; float y = -0.3f; Assert.Equal(0xda0d, new NormalizedByte2(x, y).PackedValue); - NormalizedByte2 n = new NormalizedByte2(); + var n = new NormalizedByte2(); n.PackFromRgba32(new Rgba32(141, 90, 0, 0)); Assert.Equal(0xda0d, n.PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new NormalizedByte2(x, y).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 141, 90, 0 }); + new NormalizedByte2(x, y).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(141, 90, 0)); - new NormalizedByte2(x, y).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 141, 90, 0, 255 }); + new NormalizedByte2(x, y).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(141, 90, 0, 255)); - new NormalizedByte2(x, y).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 90, 141 }); + new NormalizedByte2(x, y).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(141, 90, 0)); - new NormalizedByte2(x, y).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 90, 141, 255 }); + new NormalizedByte2(x, y).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(141, 90, 0, 255)); } [Fact] @@ -490,38 +485,38 @@ namespace ImageSharp.Tests.Colors float z = 0.5f; float w = -0.7f; Assert.Equal(0xA740DA0D, new NormalizedByte4(x, y, z, w).PackedValue); - NormalizedByte4 n = new NormalizedByte4(); + var n = new NormalizedByte4(); n.PackFromRgba32(new Rgba32(141, 90, 192, 39)); Assert.Equal(0xA740DA0D, n.PackedValue); Assert.Equal((uint)958796544, new NormalizedByte4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new NormalizedByte4(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 141, 90, 192 }); + new NormalizedByte4(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(141, 90, 192)); - new NormalizedByte4(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 141, 90, 192, 39 }); + new NormalizedByte4(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); - new NormalizedByte4(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 192, 90, 141 }); + new NormalizedByte4(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(141, 90, 192)); - new NormalizedByte4(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 }); + new NormalizedByte4(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); // http://community.monogame.net/t/normalizedbyte4-texture2d-gives-different-results-from-xna/8012/8 - NormalizedByte4 r = new NormalizedByte4(); + var r = new NormalizedByte4(); r.PackFromRgba32(new Rgba32(9, 115, 202, 127)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); r.PackedValue = 0xff4af389; - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); } [Fact] @@ -548,30 +543,30 @@ namespace ImageSharp.Tests.Colors y = -0.3f; Assert.Equal(3650751693, new NormalizedShort2(x, y).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - NormalizedShort2 n = new NormalizedShort2(); + var n = new NormalizedShort2(); n.PackFromRgba32(new Rgba32(141, 90, 0, 0)); - n.ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 141, 90, 0 }); + n.ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(141, 90, 0)); // TODO: I don't think this can ever pass since the bytes are already truncated. // Assert.Equal(3650751693, n.PackedValue); - new NormalizedShort2(x, y).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 141, 90, 0 }); + new NormalizedShort2(x, y).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(141, 90, 0)); - new NormalizedShort2(x, y).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 141, 90, 0, 255 }); + new NormalizedShort2(x, y).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(141, 90, 0, 255)); - new NormalizedShort2(x, y).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 90, 141 }); + new NormalizedShort2(x, y).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(141, 90, 0)); - new NormalizedShort2(x, y).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 90, 141, 255 }); + new NormalizedShort2(x, y).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(141, 90, 0, 255)); } [Fact] @@ -597,27 +592,27 @@ namespace ImageSharp.Tests.Colors Assert.Equal(0xa6674000d99a0ccd, new NormalizedShort4(x, y, z, w).PackedValue); Assert.Equal((ulong)4150390751449251866, new NormalizedShort4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new NormalizedShort4(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 141, 90, 192 }); + new NormalizedShort4(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(141, 90, 192)); - new NormalizedShort4(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 141, 90, 192, 39 }); + new NormalizedShort4(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); - new NormalizedShort4(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 192, 90, 141 }); + new NormalizedShort4(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(141, 90, 192)); - new NormalizedShort4(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 }); + new NormalizedShort4(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); - NormalizedShort4 r = new NormalizedShort4(); + var r = new NormalizedShort4(); r.PackFromRgba32(new Rgba32(9, 115, 202, 127)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); } [Fact] @@ -644,22 +639,22 @@ namespace ImageSharp.Tests.Colors Assert.Equal((uint)6554, new Rg32(x, y).PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Rg32(x, y).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 25, 0, 0 }); + new Rg32(x, y).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(25, 0, 0)); - new Rg32(x, y).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 25, 0, 0, 255 }); + new Rg32(x, y).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(25, 0, 0, 255)); - new Rg32(x, y).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 0, 25 }); + new Rg32(x, y).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(25, 0, 0)); - new Rg32(x, y).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 0, 25, 255 }); + new Rg32(x, y).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(25, 0, 0, 255)); } [Fact] @@ -689,28 +684,28 @@ namespace ImageSharp.Tests.Colors w = -0.7f; Assert.Equal((uint)536871014, new Rgba1010102(x, y, z, w).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Rgba1010102(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 25, 0, 128 }); + new Rgba1010102(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(25, 0, 128)); - new Rgba1010102(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 }); + new Rgba1010102(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(25, 0, 128, 0)); - new Rgba1010102(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 128, 0, 25 }); + new Rgba1010102(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(25, 0, 128)); - new Rgba1010102(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 128, 0, 25, 0 }); + new Rgba1010102(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(25, 0, 128, 0)); // Alpha component accuracy will be awful. - Rgba1010102 r = new Rgba1010102(); + var r = new Rgba1010102(); r.PackFromRgba32(new Rgba32(25, 0, 128, 0)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(25, 0, 128, 0)); } [Fact] @@ -736,26 +731,26 @@ namespace ImageSharp.Tests.Colors float y = -0.3f; float z = +0.5f; float w = -0.7f; - Rgba32 rgba32 = new Rgba32(x, y, z, w); + var rgba32 = new Rgba32(x, y, z, w); Assert.Equal(0x80001Au, rgba32.PackedValue); // Test ordering - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - rgba32.ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 }); + rgba32.ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(0x1a, 0, 0x80)); - rgba32.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 }); + rgba32.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(0x1a, 0, 0x80, 0)); - rgba32.ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a }); + rgba32.ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(0x1a, 0, 0x80)); - rgba32.ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 }); + rgba32.ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(0x1a, 0, 0x80, 0)); } [Fact] @@ -783,27 +778,27 @@ namespace ImageSharp.Tests.Colors float w = 0.45f; Assert.Equal((ulong)0x73334CCC2666147B, new Rgba64(x, y, z, w).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Rgba64(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 20, 38, 76 }); + new Rgba64(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(20, 38, 76)); - new Rgba64(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 }); + new Rgba64(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(20, 38, 76, 115)); - new Rgba64(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 76, 38, 20 }); + new Rgba64(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(20, 38, 76)); - new Rgba64(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 76, 38, 20, 115 }); + new Rgba64(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(20, 38, 76, 115)); - Rgba64 r = new Rgba64(); + var r = new Rgba64(); r.PackFromRgba32(new Rgba32(20, 38, 76, 115)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(20, 38, 76, 115)); } [Fact] @@ -838,27 +833,27 @@ namespace ImageSharp.Tests.Colors y = -5.3f; Assert.Equal(4294639744, new Short2(x, y).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Short2(x, y).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 128, 127, 0 }); + new Short2(x, y).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(128, 127, 0)); - new Short2(x, y).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 128, 127, 0, 255 }); + new Short2(x, y).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(128, 127, 0, 255)); - new Short2(x, y).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 0, 127, 128 }); + new Short2(x, y).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(128, 127, 0)); - new Short2(x, y).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 0, 127, 128, 255 }); + new Short2(x, y).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(128, 127, 0, 255)); - Short2 r = new Short2(); + var r = new Short2(); r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(20, 38, 0, 255)); } [Fact] @@ -895,27 +890,27 @@ namespace ImageSharp.Tests.Colors w = 193; Assert.Equal((ulong)0x00c173b7316d2d1b, new Short4(x, y, z, w).PackedValue); - byte[] rgb = new byte[3]; - byte[] rgba = new byte[4]; - byte[] bgr = new byte[3]; - byte[] bgra = new byte[4]; + var rgb = default(Rgb24); + var rgba = default(Rgba32); + var bgr = default(Bgr24); + var bgra = default(Bgra32); - new Short4(x, y, z, w).ToXyzBytes(rgb, 0); - Assert.Equal(rgb, new byte[] { 172, 177, 243 }); + new Short4(x, y, z, w).ToRgb24(ref rgb); + Assert.Equal(rgb, new Rgb24(172, 177, 243)); - new Short4(x, y, z, w).ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 172, 177, 243, 128 }); + new Short4(x, y, z, w).ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(172, 177, 243, 128)); - new Short4(x, y, z, w).ToZyxBytes(bgr, 0); - Assert.Equal(bgr, new byte[] { 243, 177, 172 }); + new Short4(x, y, z, w).ToBgr24(ref bgr); + Assert.Equal(bgr, new Bgr24(172, 177, 243)); - new Short4(x, y, z, w).ToZyxwBytes(bgra, 0); - Assert.Equal(bgra, new byte[] { 243, 177, 172, 128 }); + new Short4(x, y, z, w).ToBgra32(ref bgra); + Assert.Equal(bgra, new Bgra32(172, 177, 243, 128)); - Short4 r = new Short4(); + var r = new Short4(); r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); - r.ToXyzwBytes(rgba, 0); - Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 }); + r.ToRgba32(ref rgba); + Assert.Equal(rgba, new Rgba32(20, 38, 0, 255)); } // Comparison helpers with small tolerance to allow for floating point rounding during computations. diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 97d5505923..9aa2e01a67 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.PixelFormats.PixelBlenders -{ - using System; - using System.Collections.Generic; - using System.Numerics; - using System.Text; - using ImageSharp.PixelFormats.PixelBlenders; - using ImageSharp.Tests.TestUtilities; - using Xunit; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ public class PorterDuffFunctionsTests { public static TheoryData NormalBlendFunctionData = new TheoryData() { diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index b2a663d074..b95f8fdf61 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.PixelFormats.PixelBlenders -{ - using System; - using System.Collections.Generic; - using System.Numerics; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.PixelFormats.PixelBlenders; - using ImageSharp.Tests.TestUtilities; - using Xunit; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ public class PorterDuffFunctionsTests_TPixel { private static Span AsSpan(T value) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index 6bcada0ff5..524747afec 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.PixelFormats -{ - using System; - using System.Collections.Generic; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.PixelFormats.PixelBlenders; - using ImageSharp.Tests.TestUtilities; - using Xunit; +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; - public partial class PixelOperationsTests +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public class PixelBlenderTests { diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 0a121cfce9..0fde67d28e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -1,22 +1,20 @@ -// ReSharper disable InconsistentNaming -// ReSharper disable AccessToDisposedClosure -namespace ImageSharp.Tests.PixelFormats -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; - using Xunit.Abstractions; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ public partial class PixelOperationsTests { - - public class Color32 : PixelOperationsTests + public class Rgba32 : PixelOperationsTests { - public Color32(ITestOutputHelper output) + public Rgba32(ITestOutputHelper output) : base(output) { } @@ -27,19 +25,19 @@ namespace ImageSharp.Tests.PixelFormats [Fact] public void IsSpecialImplementation() { - Assert.IsType(PixelOperations.Instance); + Assert.IsType(PixelOperations.Instance); } [Fact] public void ToVector4SimdAligned() { - Rgba32[] source = CreatePixelTestData(64); + ImageSharp.Rgba32[] source = CreatePixelTestData(64); Vector4[] expected = CreateExpectedVector4Data(source); TestOperation( source, expected, - (s, d) => Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) + (s, d) => ImageSharp.Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) ); } @@ -52,23 +50,23 @@ namespace ImageSharp.Tests.PixelFormats int times = 200000; int count = 1024; - using (Buffer source = new Buffer(count)) + using (Buffer source = new Buffer(count)) using (Buffer dest = new Buffer(count)) { this.Measure( times, () => { - PixelOperations.Instance.ToVector4(source, dest, count); + PixelOperations.Instance.ToVector4(source, dest, count); }); } } } - public class Argb : PixelOperationsTests + public class Argb32 : PixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public Argb(ITestOutputHelper output) + public Argb32(ITestOutputHelper output) : base(output) { } @@ -175,11 +173,15 @@ namespace ImageSharp.Tests.PixelFormats { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 3]; + var rgb = default(Rgb24); for (int i = 0; i < count; i++) { int i3 = i * 3; - source[i].ToXyzBytes(expected, i3); + source[i].ToRgb24(ref rgb); + expected[i3] = rgb.R; + expected[i3 + 1] = rgb.G; + expected[i3 + 2] = rgb.B; } TestOperation( @@ -216,11 +218,16 @@ namespace ImageSharp.Tests.PixelFormats { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; + var rgba = default(Rgba32); for (int i = 0; i < count; i++) { int i4 = i * 4; - source[i].ToXyzwBytes(expected, i4); + source[i].ToRgba32(ref rgba); + expected[i4] = rgba.R; + expected[i4 + 1] = rgba.G; + expected[i4 + 2] = rgba.B; + expected[i4 + 3] = rgba.A; } TestOperation( @@ -257,11 +264,15 @@ namespace ImageSharp.Tests.PixelFormats { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 3]; + var bgr = default(Bgr24); for (int i = 0; i < count; i++) { int i3 = i * 3; - source[i].ToZyxBytes(expected, i3); + source[i].ToBgr24(ref bgr); + expected[i3] = bgr.B; + expected[i3 + 1] = bgr.G; + expected[i3 + 2] = bgr.R; } TestOperation( @@ -298,11 +309,16 @@ namespace ImageSharp.Tests.PixelFormats { TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; + var bgra = default(Bgra32); for (int i = 0; i < count; i++) { int i4 = i * 4; - source[i].ToZyxwBytes(expected, i4); + source[i].ToBgra32(ref bgra); + expected[i4] = bgra.B; + expected[i4 + 1] = bgra.G; + expected[i4 + 2] = bgra.R; + expected[i4 + 3] = bgra.A; } TestOperation( diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index 1d0d024fbb..4e85fe7e32 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -1,13 +1,13 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class Rgb24Tests { public static readonly TheoryData ColorData = diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 5509cbddcf..a8d38b9381 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index 1f5df6d880..1dd280bee4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs index 25a61453b2..1fca398fcd 100644 --- a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs @@ -1,18 +1,19 @@ -namespace ImageSharp.Tests.Colors -{ - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ public class UnPackedPixelTests { [Fact] public void Color_Types_From_Bytes_Produce_Equal_Scaled_Component_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -23,8 +24,8 @@ [Fact] public void Color_Types_From_Floats_Produce_Equal_Scaled_Component_OutPut() { - Rgba32 color = new Rgba32(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); - RgbaVector colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + var color = new Rgba32(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -35,8 +36,8 @@ [Fact] public void Color_Types_From_Vector4_Produce_Equal_Scaled_Component_OutPut() { - Rgba32 color = new Rgba32(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); - RgbaVector colorVector = new RgbaVector(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + var color = new Rgba32(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + var colorVector = new RgbaVector(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -47,8 +48,8 @@ [Fact] public void Color_Types_From_Vector3_Produce_Equal_Scaled_Component_OutPut() { - Rgba32 color = new Rgba32(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); - RgbaVector colorVector = new RgbaVector(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + var color = new Rgba32(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + var colorVector = new RgbaVector(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -59,8 +60,8 @@ [Fact] public void Color_Types_From_Hex_Produce_Equal_Scaled_Component_OutPut() { - Rgba32 color = Rgba32.FromHex("183060C0"); - RgbaVector colorVector = RgbaVector.FromHex("183060C0"); + var color = Rgba32.FromHex("183060C0"); + var colorVector = RgbaVector.FromHex("183060C0"); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -71,8 +72,8 @@ [Fact] public void Color_Types_To_Vector4_Produce_Equal_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); Assert.Equal(color.ToVector4(), colorVector.ToVector4()); } @@ -80,14 +81,14 @@ [Fact] public void Color_Types_To_RgbBytes_Produce_Equal_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); - byte[] rgb = new byte[3]; - byte[] rgbVector = new byte[3]; + var rgb = default(Rgb24); + var rgbVector = default(Rgb24); - color.ToXyzBytes(rgb, 0); - colorVector.ToXyzBytes(rgbVector, 0); + color.ToRgb24(ref rgb); + colorVector.ToRgb24(ref rgbVector); Assert.Equal(rgb, rgbVector); } @@ -95,14 +96,14 @@ [Fact] public void Color_Types_To_RgbaBytes_Produce_Equal_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); - byte[] rgba = new byte[4]; - byte[] rgbaVector = new byte[4]; + var rgba = default(Rgba32); + var rgbaVector = default(Rgba32); - color.ToXyzwBytes(rgba, 0); - colorVector.ToXyzwBytes(rgbaVector, 0); + color.ToRgba32(ref rgba); + colorVector.ToRgba32(ref rgbaVector); Assert.Equal(rgba, rgbaVector); } @@ -110,14 +111,14 @@ [Fact] public void Color_Types_To_BgrBytes_Produce_Equal_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); - byte[] bgr = new byte[3]; - byte[] bgrVector = new byte[3]; + var bgr = default(Bgr24); + var bgrVector = default(Bgr24); - color.ToZyxBytes(bgr, 0); - colorVector.ToZyxBytes(bgrVector, 0); + color.ToBgr24(ref bgr); + colorVector.ToBgr24(ref bgrVector); Assert.Equal(bgr, bgrVector); } @@ -125,14 +126,14 @@ [Fact] public void Color_Types_To_BgraBytes_Produce_Equal_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); - byte[] bgra = new byte[4]; - byte[] bgraVector = new byte[4]; + var bgra = default(Bgra32); + var bgraVector = default(Bgra32); - color.ToZyxwBytes(bgra, 0); - colorVector.ToZyxwBytes(bgraVector, 0); + color.ToBgra32(ref bgra); + colorVector.ToBgra32(ref bgraVector); Assert.Equal(bgra, bgraVector); } @@ -140,8 +141,8 @@ [Fact] public void Color_Types_To_Hex_Produce_Equal_OutPut() { - Rgba32 color = new Rgba32(24, 48, 96, 192); - RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + var color = new Rgba32(24, 48, 96, 192); + var colorVector = new RgbaVector(24, 48, 96, 192); // 183060C0 Assert.Equal(color.ToHex(), colorVector.ToHex()); diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs index 642df598a3..221b4a9bff 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs @@ -1,51 +1,30 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Binarization -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BinaryThresholdTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class BinaryThresholdTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData BinaryThresholdValues - = new TheoryData - { - .25F, - .75F - }; - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] - public void ImageShouldApplyBinaryThresholdFilter(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [Fact] + public void BinaryThreshold_CorrectProcessor() { - using (Image image = provider.GetImage()) - { - image.BinaryThreshold(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.BinaryThreshold(.23f); + var p = this.Verify>(); + Assert.Equal(.23f, p.Threshold); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] - public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [Fact] + public void BinaryThreshold_rect_CorrectProcessor() { - - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.BinaryThreshold(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.BinaryThreshold(.93f, this.rect); + var p = this.Verify>(this.rect); + Assert.Equal(.93f, p.Threshold); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs deleted file mode 100644 index a06397c864..0000000000 --- a/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.Binarization -{ - using ImageSharp.Dithering; - using ImageSharp.Dithering.Ordered; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class DitherTest : FileTestBase - { - public static readonly TheoryData Ditherers = new TheoryData - { - { "Ordered", new Ordered() }, - { "Bayer", new Bayer() } - }; - - public static readonly TheoryData ErrorDiffusers = new TheoryData - { - { "Atkinson", new Atkinson() }, - { "Burks", new Burks() }, - { "FloydSteinberg", new FloydSteinberg() }, - { "JarvisJudiceNinke", new JarvisJudiceNinke() }, - { "Sierra2", new Sierra2() }, - { "Sierra3", new Sierra3() }, - { "SierraLite", new SierraLite() }, - { "Stucki", new Stucki() }, - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(Ditherers), DefaultPixelType)] - public void ImageShouldApplyDitherFilter(TestImageProvider provider, string name, IOrderedDither ditherer) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Dither(ditherer) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(Ditherers), DefaultPixelType)] - public void ImageShouldApplyDitherFilterInBox(TestImageProvider provider, string name, IOrderedDither ditherer) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Dither(ditherer, bounds) - .DebugSave(provider, name, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ErrorDiffusers), DefaultPixelType)] - public void ImageShouldApplyDiffusionFilter(TestImageProvider provider, string name, IErrorDiffuser diffuser) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Dither(diffuser, .5F) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ErrorDiffusers), DefaultPixelType)] - public void ImageShouldApplyDiffusionFilterInBox(TestImageProvider provider, string name, IErrorDiffuser diffuser) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Dither(diffuser,.5F, bounds) - .DebugSave(provider, name, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs new file mode 100644 index 0000000000..94241d0071 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Moq; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class DitherTest : BaseImageOperationsExtensionTest + { + private readonly IOrderedDither orderedDither; + private readonly IErrorDiffuser errorDiffuser; + + public DitherTest() + { + this.orderedDither = new Mock().Object; + this.errorDiffuser = new Mock().Object; + } + [Fact] + public void Dither_CorrectProcessor() + { + this.operations.Dither(orderedDither); + var p = this.Verify>(); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(0, p.Index); + } + + [Fact] + public void Dither_rect_CorrectProcessor() + { + this.operations.Dither(orderedDither, this.rect); + var p = this.Verify>(this.rect); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(0, p.Index); + } + [Fact] + public void Dither_index_CorrectProcessor() + { + this.operations.Dither(orderedDither, 2); + var p = this.Verify>(); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(2, p.Index); + } + + [Fact] + public void Dither_index_rect_CorrectProcessor() + { + this.operations.Dither(orderedDither, this.rect, 2); + var p = this.Verify>(this.rect); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(2, p.Index); + } + + + [Fact] + public void Dither_ErrorDifuser_CorrectProcessor() + { + this.operations.Dither(errorDiffuser, 4); + var p = this.Verify>(); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(4, p.Threshold); + } + + [Fact] + public void Dither_ErrorDifuser_rect_CorrectProcessor() + { + this.operations.Dither(this.errorDiffuser, 3, this.rect); + var p = this.Verify>(this.rect); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(3, p.Threshold); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs deleted file mode 100644 index 4dc70fb0f5..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class BlackWhiteTest : FileTestBase - { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBlackWhiteFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.BlackWhite() - .DebugSave(provider, null, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBlackWhiteFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.BlackWhite(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs deleted file mode 100644 index bc1c8ad226..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; - - public class ColorBlindnessTest : FileTestBase - { - public static readonly TheoryData ColorBlindnessFilters - = new TheoryData - { - ColorBlindness.Achromatomaly, - ColorBlindness.Achromatopsia, - ColorBlindness.Deuteranomaly, - ColorBlindness.Deuteranopia, - ColorBlindness.Protanomaly, - ColorBlindness.Protanopia, - ColorBlindness.Tritanomaly, - ColorBlindness.Tritanopia - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] - public void ImageShouldApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindness colorBlindness) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.ColorBlindness(colorBlindness) - .DebugSave(provider, colorBlindness.ToString(), Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] - public void ImageShouldApplyColorBlindnessFilterInBox(TestImageProvider provider, ColorBlindness colorBlindness) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.ColorBlindness(colorBlindness, bounds) - .DebugSave(provider, colorBlindness.ToString(), Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs deleted file mode 100644 index 9bf55a6d25..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; - - public class GrayscaleTest : FileTestBase - { - public static readonly TheoryData GrayscaleModeTypes - = new TheoryData - { - GrayscaleMode.Bt601, - GrayscaleMode.Bt709 - }; - - /// - /// Use test patterns over loaded images to save decode time. - /// - [Theory] - [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] - public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Grayscale(value); - byte[] data = new byte[3]; - for (int i = 0; i < image.Pixels.Length; i++) - { - image.Pixels[i].ToXyzBytes(data, 0); - Assert.Equal(data[0], data[1]); - Assert.Equal(data[1], data[2]); - } - - image.DebugSave(provider, value.ToString()); - } - } - - [Theory] - [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] - public void ImageShouldApplyGrayscaleFilterInBox(TestImageProvider provider, GrayscaleMode value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - image.Grayscale(value, bounds) - .DebugSave(provider, value.ToString()); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs deleted file mode 100644 index ba1cf2b8f7..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class HueTest : FileTestBase - { - public static readonly TheoryData HueValues - = new TheoryData - { - 180, - -180 - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] - public void ImageShouldApplyHueFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Hue(value) - .DebugSave(provider, value, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] - public void ImageShouldApplyHueFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Hue(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs deleted file mode 100644 index 971cdb6d81..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class KodachromeTest : FileTestBase - { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyKodachromeFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Kodachrome() - .DebugSave(provider, null, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyKodachromeFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Kodachrome(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs deleted file mode 100644 index 5b41cdb3b7..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class LomographTest : FileTestBase - { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyLomographFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Lomograph() - .DebugSave(provider, null, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyLomographFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Lomograph(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs deleted file mode 100644 index 48f7a6c0a8..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class PolaroidTest : FileTestBase - { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyPolaroidFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Polaroid() - .DebugSave(provider, null, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyPolaroidFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Polaroid(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs deleted file mode 100644 index a4a4f3bb51..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class SaturationTest : FileTestBase - { - public static readonly TheoryData SaturationValues - = new TheoryData - { - 50 , - -50 , - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] - public void ImageShouldApplySaturationFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Saturation(value) - .DebugSave(provider, value, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] - public void ImageShouldApplySaturationFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Saturation(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs deleted file mode 100644 index af554e30d8..0000000000 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class SepiaTest : FileTestBase - { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplySepiaFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Sepia() - .DebugSave(provider, null, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplySepiaFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Sepia(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index ef049a539a..cd0e8e5ab0 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -1,50 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BoxBlurTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class BoxBlurTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData BoxBlurValues - = new TheoryData + [Fact] + public void BoxBlur_BoxBlurProcessorDefaultsSet() { - 3, - 5 - }; + this.operations.BoxBlur(); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.BoxBlur(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(7, processor.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void BoxBlur_amount_BoxBlurProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.BoxBlur(34); + var processor = this.Verify>(); - image.BoxBlur(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + Assert.Equal(34, processor.Radius); + } + + [Fact] + public void BoxBlur_amount_rect_BoxBlurProcessorDefaultsSet() + { + this.operations.BoxBlur(5, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(5, processor.Radius); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index d5817ab14f..b52938aac1 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -1,59 +1,74 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; - public class DetectEdgesTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class DetectEdgesTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData DetectEdgesFilters - = new TheoryData + + [Fact] + public void DetectEdges_SobelProcessorDefaultsSet() { - EdgeDetection.Kayyali, - EdgeDetection.Kirsch, - EdgeDetection.Lapacian3X3, - EdgeDetection.Lapacian5X5, - EdgeDetection.LaplacianOfGaussian, - EdgeDetection.Prewitt, - EdgeDetection.RobertsCross, - EdgeDetection.Robinson, - EdgeDetection.Scharr, - EdgeDetection.Sobel + this.operations.DetectEdges(); + + // TODO: Enable once we have updated the images + // SobelProcessor processor = this.Verify>(); + // Assert.True(processor.Grayscale); + } + + [Fact] + public void DetectEdges_Rect_SobelProcessorDefaultsSet() + { + this.operations.DetectEdges(this.rect); + + // TODO: Enable once we have updated the images + // SobelProcessor processor = this.Verify>(this.rect); + // Assert.True(processor.Grayscale); + } + public static IEnumerable EdgeDetectionTheoryData => new[] { + new object[]{ new TestType>(), EdgeDetection.Kayyali }, + new object[]{ new TestType>(), EdgeDetection.Kirsch }, + new object[]{ new TestType>(), EdgeDetection.Lapacian3X3 }, + new object[]{ new TestType>(), EdgeDetection.Lapacian5X5 }, + new object[]{ new TestType>(), EdgeDetection.LaplacianOfGaussian }, + new object[]{ new TestType>(), EdgeDetection.Prewitt }, + new object[]{ new TestType>(), EdgeDetection.RobertsCross }, + new object[]{ new TestType>(), EdgeDetection.Robinson }, + new object[]{ new TestType>(), EdgeDetection.Scharr }, + new object[]{ new TestType>(), EdgeDetection.Sobel }, }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(DetectEdgesFilters), DefaultPixelType)] - public void ImageShouldApplyDetectEdgesFilter(TestImageProvider provider, EdgeDetection detector) - where TPixel : struct, IPixel + [MemberData(nameof(EdgeDetectionTheoryData))] + public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetection filter) + where TProcessor : IEdgeDetectorProcessor { - using (Image image = provider.GetImage()) - { - image.DetectEdges(detector) - .DebugSave(provider, detector.ToString(), Extensions.Bmp); - } + this.operations.DetectEdges(filter); + + // TODO: Enable once we have updated the images + // var processor = this.Verify(); + // Assert.True(processor.Grayscale); } [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(DetectEdgesFilters), DefaultPixelType)] - public void ImageShouldApplyDetectEdgesFilterInBox(TestImageProvider provider, EdgeDetection detector) - where TPixel : struct, IPixel + [MemberData(nameof(EdgeDetectionTheoryData))] + public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetection filter) + where TProcessor : IEdgeDetectorProcessor { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.DetectEdges(detector, bounds) - .DebugSave(provider, detector.ToString(), Extensions.Bmp); + bool grey = (int)filter % 2 == 0; + this.operations.DetectEdges(filter, grey); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + // TODO: Enable once we have updated the images + // var processor = this.Verify() + // Assert.Equal(grey, processor.Grayscale); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index fa4e4b0b6b..d0773a0bfe 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -1,50 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class GaussianBlurTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class GaussianBlurTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData GaussianBlurValues - = new TheoryData + [Fact] + public void GaussianBlur_GaussianBlurProcessorDefaultsSet() { - 3, - 5 - }; + this.operations.GaussianBlur(); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.GaussianBlur(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(3f, processor.Sigma); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.GaussianBlur(0.2f); + var processor = this.Verify>(); - image.GaussianBlur(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + Assert.Equal(.2f, processor.Sigma); + } + + [Fact] + public void GaussianBlur_amount_rect_GaussianBlurProcessorDefaultsSet() + { + this.operations.GaussianBlur(0.6f, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(.6f, processor.Sigma); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index d15042ee69..c7c78a21a6 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -1,50 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class GaussianSharpenTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class GaussianSharpenTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData GaussianSharpenValues - = new TheoryData + [Fact] + public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() { - 3, - 5 - }; + this.operations.GaussianSharpen(); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.GaussianSharpen(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(3f, processor.Sigma); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.GaussianSharpen(0.2f); + var processor = this.Verify>(); - image.GaussianSharpen(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + Assert.Equal(.2f, processor.Sigma); + } + + [Fact] + public void GaussianSharpen_amount_rect_GaussianSharpenProcessorDefaultsSet() + { + this.operations.GaussianSharpen(0.6f, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(.6f, processor.Sigma); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/DelegateTest.cs b/tests/ImageSharp.Tests/Processing/DelegateTest.cs new file mode 100644 index 0000000000..518a28bea0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/DelegateTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing +{ + public class DelegateTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Run_CreatedDelegateProcessor() + { + Action> action = (i) => { }; + this.operations.Apply(action); + + DelegateProcessor processor = this.Verify>(); + Assert.Equal(action, processor.Action); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs b/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs deleted file mode 100644 index 34a24e70be..0000000000 --- a/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class AlphaTest : FileTestBase - { - public static readonly TheoryData AlphaValues - = new TheoryData - { - 20/100F, - 80/100F - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] - public void ImageShouldApplyAlphaFilter(TestImageProvider provider, float value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Alpha(value) - .DebugSave(provider, value, Extensions.Png); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] - public void ImageShouldApplyAlphaFilterInBox(TestImageProvider provider, float value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Alpha(value, bounds) - .DebugSave(provider, value, Extensions.Png); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs index 63efbf3e73..a24b8a4d47 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -1,43 +1,53 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BackgroundColorTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class BackgroundColorTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBackgroundColorFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet() + { + this.operations.BackgroundColor(Rgba32.BlanchedAlmond); + var processor = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); + } + + [Fact] + public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.BackgroundColor(NamedColors.HotPink) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.rect); + var processor = this.Verify>(this.rect); + + Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBackgroundColorFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.options); + var processor = this.Verify>(); - image.BackgroundColor(NamedColors.HotPink, bounds) - .DebugSave(provider, null, Extensions.Bmp); + Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); + } + + [Fact] + public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet() + { + this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.rect, this.options); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs deleted file mode 100644 index c3b37705a9..0000000000 --- a/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class BrightnessTest : FileTestBase - { - public static readonly TheoryData BrightnessValues - = new TheoryData - { - 50, - -50 - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] - public void ImageShouldApplyBrightnessFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Brightness(value) - .DebugSave(provider, value, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] - public void ImageShouldApplyBrightnessFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Brightness(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); ; - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs deleted file mode 100644 index 892eb93622..0000000000 --- a/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class ContrastTest : FileTestBase - { - public static readonly TheoryData ContrastValues - = new TheoryData - { - 50, - -50 - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] - public void ImageShouldApplyContrastFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Contrast(value) - .DebugSave(provider, value, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] - public void ImageShouldApplyContrastFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Contrast(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs deleted file mode 100644 index 3d8b3d1a1f..0000000000 --- a/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class InvertTest : FileTestBase - { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyInvertFilter(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Invert() - .DebugSave(provider, null, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyInvertFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Invert(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs index 3d0e8f1177..36de67188f 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -1,50 +1,52 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class OilPaintTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class OilPaintTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData OilPaintValues - = new TheoryData - { - { 15, 10 }, - { 6, 5 } - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] - public void ImageShouldApplyOilPaintFilter(TestImageProvider provider, int levels, int brushSize) - where TPixel : struct, IPixel + [Fact] + public void OilPaint_OilPaintingProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.OilPaint(levels, brushSize) - .DebugSave(provider, string.Join("-", levels, brushSize), Extensions.Bmp); - } + this.operations.OilPaint(); + var processor = this.Verify>(); + + Assert.Equal(10, processor.Levels); + Assert.Equal(15, processor.BrushSize); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] - public void ImageShouldApplyOilPaintFilterInBox(TestImageProvider provider, int levels, int brushSize) - where TPixel : struct, IPixel + [Fact] + public void OilPaint_rect_OilPaintingProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.OilPaint(this.rect); + var processor = this.Verify>(this.rect); - image.OilPaint(levels, brushSize, bounds) - .DebugSave(provider, string.Join("-", levels, brushSize), Extensions.Bmp); + Assert.Equal(10, processor.Levels); + Assert.Equal(15, processor.BrushSize); + } + [Fact] + public void OilPaint_Levels_Brsuh_OilPaintingProcessorDefaultsSet() + { + this.operations.OilPaint(34, 65); + var processor = this.Verify>(); + + Assert.Equal(34, processor.Levels); + Assert.Equal(65, processor.BrushSize); + } + + [Fact] + public void OilPaint_Levels_Brsuh_rect_OilPaintingProcessorDefaultsSet() + { + this.operations.OilPaint(54, 43, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F); - } + Assert.Equal(54, processor.Levels); + Assert.Equal(43, processor.BrushSize); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs index 253f1f4598..8b50e14861 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -1,84 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class PixelateTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class PixelateTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData PixelateValues - = new TheoryData - { - 4 , - 8 - }; - - [Theory] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] - public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Pixelate_PixelateProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Pixelate(value) - .DebugSave(provider, value, Extensions.Bmp); + this.operations.Pixelate(); + var processor = this.Verify>(); - // Test the neigbouring pixels - for (int y = 0; y < image.Height; y += value) - { - for (int x = 0; x < image.Width; x += value) - { - TPixel source = image[x, y]; - for (int pixY = y; pixY < y + value && pixY < image.Height; pixY++) - { - for (int pixX = x; pixX < x + value && pixX < image.Width; pixX++) - { - Assert.Equal(source, image[pixX, pixY]); - } - } - } - } - } + Assert.Equal(4, processor.Size); } - [Theory] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] - public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Pixelate_Size_PixelateProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + this.operations.Pixelate(12); + var processor = this.Verify>(); - image.Pixelate(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - for (int y = 0; y < image.Height; y++) - { - for (int x = 0; x < image.Width; x++) - { - int tx = x; - int ty = y; - TPixel sourceColor = source[tx, ty]; - if (bounds.Contains(tx, ty)) - { - int sourceX = tx - ((tx - bounds.Left) % value) + (value / 2); - int sourceY = ty - ((ty - bounds.Top) % value) + (value / 2); + Assert.Equal(12, processor.Size); + } - sourceColor = image[sourceX, sourceY]; - } - Assert.Equal(sourceColor, image[tx, ty]); - } - } + [Fact] + public void Pixelate_Size_rect_PixelateProcessorDefaultsSet() + { + this.operations.Pixelate(23, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(23, processor.Size); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs new file mode 100644 index 0000000000..4ade9fe64b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class BlackWhiteTest : BaseImageOperationsExtensionTest + { + [Fact] + public void BlackWhite_CorrectProcessor() + { + this.operations.BlackWhite(); + BlackWhiteProcessor p = this.Verify>(); + } + + [Fact] + public void BlackWhite_rect_CorrectProcessor() + { + this.operations.BlackWhite( this.rect); + BlackWhiteProcessor p = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs new file mode 100644 index 0000000000..05605767fb --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class BrightnessTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Brightness_amount_BrightnessProcessorDefaultsSet() + { + this.operations.Brightness(1.5F); + BrightnessProcessor processor = this.Verify>(); + + Assert.Equal(1.5F, processor.Amount); + } + + [Fact] + public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() + { + this.operations.Brightness(1.5F, this.rect); + BrightnessProcessor processor = this.Verify>(this.rect); + + Assert.Equal(1.5F, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs new file mode 100644 index 0000000000..2dc695f560 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class ColorBlindnessTest : BaseImageOperationsExtensionTest + { + public static IEnumerable TheoryData = new[] { + new object[]{ new TestType>(), ColorBlindness.Achromatomaly }, + new object[]{ new TestType>(), ColorBlindness.Achromatopsia }, + new object[]{ new TestType>(), ColorBlindness.Deuteranomaly }, + new object[]{ new TestType>(), ColorBlindness.Deuteranopia }, + new object[]{ new TestType>(), ColorBlindness.Protanomaly }, + new object[]{ new TestType>(), ColorBlindness.Protanopia }, + new object[]{ new TestType>(), ColorBlindness.Tritanomaly }, + new object[]{ new TestType>(), ColorBlindness.Tritanopia } + }; + + [Theory] + [MemberData(nameof(TheoryData))] + public void ColorBlindness_CorrectProcessor(TestType testType, ColorBlindness colorBlindness) + where T : IImageProcessor + { + this.operations.ColorBlindness(colorBlindness); + var p = this.Verify(); + } + [Theory] + [MemberData(nameof(TheoryData))] + public void ColorBlindness_rect_CorrectProcessor(TestType testType, ColorBlindness colorBlindness) + where T : IImageProcessor + { + this.operations.ColorBlindness(colorBlindness, this.rect); + var p = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs new file mode 100644 index 0000000000..4aec24dad0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class ContrastTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Contrast_amount_ContrastProcessorDefaultsSet() + { + this.operations.Contrast(1.5F); + ContrastProcessor processor = this.Verify>(); + + Assert.Equal(1.5F, processor.Amount); + } + + [Fact] + public void Contrast_amount_rect_ContrastProcessorDefaultsSet() + { + this.operations.Contrast(1.5F, this.rect); + ContrastProcessor processor = this.Verify>(this.rect); + + Assert.Equal(1.5F, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs new file mode 100644 index 0000000000..d1e9c0ba0a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class FilterTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Filter_CorrectProcessor() + { + this.operations.Filter(MatrixFilters.AchromatomalyFilter * MatrixFilters.CreateHueFilter(90F)); + FilterProcessor p = this.Verify>(); + } + + [Fact] + public void Filter_rect_CorrectProcessor() + { + this.operations.Filter(MatrixFilters.AchromatomalyFilter * MatrixFilters.CreateHueFilter(90F), this.rect); + FilterProcessor p = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs new file mode 100644 index 0000000000..80c377eb1d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class GrayscaleTest : BaseImageOperationsExtensionTest + { + public static IEnumerable ModeTheoryData = new[] { + new object[]{ new TestType>(), GrayscaleMode.Bt709 } + }; + + [Theory] + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor + { + this.operations.Grayscale(mode); + var p = this.Verify(); + + } + + [Theory] + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_rect_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor + { + this.operations.Grayscale(mode, this.rect); + this.Verify(this.rect); + } + + [Fact] + public void Grayscale_rect_CorrectProcessor() + { + this.operations.Grayscale(this.rect); + this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs new file mode 100644 index 0000000000..bf03ee4f81 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class HueTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Hue_amount_HueProcessorDefaultsSet() + { + this.operations.Hue(34f); + var processor = this.Verify>(); + + Assert.Equal(34f, processor.Degrees); + } + + [Fact] + public void Hue_amount_rect_HueProcessorDefaultsSet() + { + this.operations.Hue(5f, this.rect); + var processor = this.Verify>(this.rect); + + Assert.Equal(5f, processor.Degrees); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs new file mode 100644 index 0000000000..ad74b3c909 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class InvertTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Invert_InvertProcessorDefaultsSet() + { + this.operations.Invert(); + var processor = this.Verify>(); + } + + [Fact] + public void Pixelate_rect_PixelateProcessorDefaultsSet() + { + this.operations.Invert(this.rect); + var processor = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs new file mode 100644 index 0000000000..b3731d43c1 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class KodachromeTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Kodachrome_amount_KodachromeProcessorDefaultsSet() + { + this.operations.Kodachrome(); + var processor = this.Verify>(); + } + + [Fact] + public void Kodachrome_amount_rect_KodachromeProcessorDefaultsSet() + { + this.operations.Kodachrome(this.rect); + var processor = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs new file mode 100644 index 0000000000..32107cb716 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class LomographTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Lomograph_amount_LomographProcessorDefaultsSet() + { + this.operations.Lomograph(); + var processor = this.Verify>(); + } + + [Fact] + public void Lomograph_amount_rect_LomographProcessorDefaultsSet() + { + this.operations.Lomograph(this.rect); + var processor = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs new file mode 100644 index 0000000000..4108cbddac --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class OpacityTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Alpha_amount_AlphaProcessorDefaultsSet() + { + this.operations.Opacity(0.2f); + OpacityProcessor processor = this.Verify>(); + + Assert.Equal(.2f, processor.Amount); + } + + [Fact] + public void Alpha_amount_rect_AlphaProcessorDefaultsSet() + { + this.operations.Opacity(0.6f, this.rect); + OpacityProcessor processor = this.Verify>(this.rect); + + Assert.Equal(.6f, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs new file mode 100644 index 0000000000..5661851a56 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class PolaroidTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Polaroid_amount_PolaroidProcessorDefaultsSet() + { + this.operations.Polaroid(); + var processor = this.Verify>(); + } + + [Fact] + public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() + { + this.operations.Polaroid(this.rect); + var processor = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs new file mode 100644 index 0000000000..e3b9e3d3b2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class SaturateTest : BaseImageOperationsExtensionTest + { + + [Fact] + public void Saturation_amount_SaturationProcessorDefaultsSet() + { + this.operations.Saturate(34); + SaturateProcessor processor = this.Verify>(); + + Assert.Equal(34, processor.Amount); + } + + [Fact] + public void Saturation_amount_rect_SaturationProcessorDefaultsSet() + { + this.operations.Saturate(5, this.rect); + SaturateProcessor processor = this.Verify>(this.rect); + + Assert.Equal(5, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs new file mode 100644 index 0000000000..4983313882 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class SepiaTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Sepia_amount_SepiaProcessorDefaultsSet() + { + this.operations.Sepia(); + var processor = this.Verify>(); + } + + [Fact] + public void Sepia_amount_rect_SepiaProcessorDefaultsSet() + { + this.operations.Sepia(this.rect); + var processor = this.Verify>(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index d0633dca59..9e7a046507 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,67 +1,59 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Overlays -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class GlowTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Overlays +{ + public class GlowTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_GlowProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Glow() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Glow(); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterColor(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_Color_GlowProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Glow(NamedColors.Orange) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Glow(Rgba32.Aquamarine); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Aquamarine, p.GlowColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterRadius(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_Radux_GlowProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Glow(image.Width / 4F) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Glow(3.5f); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(ValueSize.Absolute(3.5f), p.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_Rect_GlowProcessorWithDefaultValues() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Glow(bounds) - .DebugSave(provider, null, Extensions.Bmp); + var rect = new Rectangle(12, 123, 43, 65); + this.operations.Glow(rect); + var p = this.Verify>(rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index 56fcf0ee0d..e082e42cd7 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -1,67 +1,64 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Overlays -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; - public class VignetteTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Overlays +{ + public class VignetteTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_VignetteProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Vignette() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Vignette(); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); + Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterColor(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_Color_VignetteProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Vignette(NamedColors.Orange) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Vignette(Rgba32.Aquamarine); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Aquamarine, p.VignetteColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); + Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterRadius(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_Radux_VignetteProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Vignette(image.Width / 4F, image.Height / 4F) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Vignette(3.5f, 12123f); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX); + Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_Rect_VignetteProcessorWithDefaultValues() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Vignette(bounds) - .DebugSave(provider, null, Extensions.Bmp); + var rect = new Rectangle(12, 123, 43, 65); + this.operations.Vignette(rect); + var p = this.Verify>(rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); + Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs new file mode 100644 index 0000000000..3daeecfc75 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization +{ + public class BinaryThresholdTest : FileTestBase + { + public static readonly TheoryData BinaryThresholdValues + = new TheoryData + { + .25F, + .75F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + public void ImageShouldApplyBinaryThresholdFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BinaryThreshold(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BinaryThreshold(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs new file mode 100644 index 0000000000..9a6d24226b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization +{ + using System.Linq; + + public class DitherTests : FileTestBase + { + public static readonly string[] CommonTestImages = + { + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + }; + + public static readonly TheoryData Ditherers = new TheoryData + { + { "Ordered", new OrderedDither() }, + { "Bayer", new BayerDither() } + }; + + public static readonly TheoryData ErrorDiffusers = new TheoryData + { + { "Atkinson", new AtkinsonDiffuser() }, + { "Burks", new BurksDiffuser() }, + { "FloydSteinberg", new FloydSteinbergDiffuser() }, + { "JarvisJudiceNinke", new JarvisJudiceNinkeDiffuser() }, + { "Sierra2", new Sierra2Diffuser() }, + { "Sierra3", new Sierra3Diffuser() }, + { "SierraLite", new SierraLiteDiffuser() }, + { "Stucki", new StuckiDiffuser() }, + }; + + + private static IOrderedDither DefaultDitherer => new OrderedDither(); + + private static IErrorDiffuser DefaultErrorDiffuser => new AtkinsonDiffuser(); + + [Theory] + [WithFileCollection(nameof(CommonTestImages), nameof(Ditherers), DefaultPixelType)] + [WithTestPatternImages(nameof(Ditherers), 100, 100, DefaultPixelType)] + public void DitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(ditherer)); + image.DebugSave(provider, name); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), DefaultPixelType)] + [WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, DefaultPixelType)] + public void DiffusionFilter_WorksWithAllErrorDiffusers(TestImageProvider provider, string name, IErrorDiffuser diffuser) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(diffuser, .5F)); + image.DebugSave(provider, name); + } + } + + [Theory] + [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(DefaultDitherer)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(DefaultErrorDiffuser, 0.5f)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + public void ApplyDitherFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Dither(DefaultDitherer, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + public void ApplyDiffusionFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Dither(DefaultErrorDiffuser, .5F, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs new file mode 100644 index 0000000000..94af7aa37e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class BoxBlurTest : FileTestBase + { + public static readonly TheoryData BoxBlurValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] + public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BoxBlur(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] + public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BoxBlur(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs new file mode 100644 index 0000000000..323842556e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -0,0 +1,85 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using SixLabors.Primitives; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class DetectEdgesTest : FileTestBase + { + public static readonly string[] CommonTestImages = { TestImages.Png.Bike }; + + public static readonly TheoryData DetectEdgesFilters = new TheoryData + { + EdgeDetection.Kayyali, + EdgeDetection.Kirsch, + EdgeDetection.Lapacian3X3, + EdgeDetection.Lapacian5X5, + EdgeDetection.LaplacianOfGaussian, + EdgeDetection.Prewitt, + EdgeDetection.RobertsCross, + EdgeDetection.Robinson, + EdgeDetection.Scharr, + EdgeDetection.Sobel + }; + + [Theory] + [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), nameof(DetectEdgesFilters), DefaultPixelType)] + public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetection detector) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges(detector)); + image.DebugSave(provider, detector.ToString()); + image.CompareToReferenceOutput(provider, detector.ToString()); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), CommonNonDefaultPixelTypes)] + public void DetectEdges_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges()); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + public void DetectEdges_IsAppliedToAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges()); + image.DebugSave(provider, extension: "gif"); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void DetectEdges_InBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.DetectEdges(bounds)); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs new file mode 100644 index 0000000000..3508d544be --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class GaussianBlurTest : FileTestBase + { + public static readonly TheoryData GaussianBlurValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] + public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.GaussianBlur(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] + public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.GaussianBlur(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs new file mode 100644 index 0000000000..4775444a5a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class GaussianSharpenTest : FileTestBase + { + public static readonly TheoryData GaussianSharpenValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] + public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.GaussianSharpen(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] + public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.GaussianSharpen(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs new file mode 100644 index 0000000000..2dd44b3a76 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class BackgroundColorTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBackgroundColorFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BackgroundColor(NamedColors.HotPink)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBackgroundColorFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BackgroundColor(NamedColors.HotPink, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs new file mode 100644 index 0000000000..608fcf10cf --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class OilPaintTest : FileTestBase + { + public static readonly TheoryData OilPaintValues + = new TheoryData + { + { 15, 10 }, + { 6, 5 } + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] + public void ApplyOilPaintFilter(TestImageProvider provider, int levels, int brushSize) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.OilPaint(levels, brushSize)); + image.DebugSave(provider, string.Join("-", levels, brushSize)); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] + public void ApplyOilPaintFilterInBox(TestImageProvider provider, int levels, int brushSize) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.OilPaint(levels, brushSize, bounds)); + image.DebugSave(provider, string.Join("-", levels, brushSize)); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs new file mode 100644 index 0000000000..b3f1ee3a28 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class PixelateTest : FileTestBase + { + public static readonly TheoryData PixelateValues + = new TheoryData + { + 4 , + 8 + }; + + [Theory] + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] + public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Pixelate(value)); + image.DebugSave(provider, value); + + // Test the neigbouring pixels + for (int y = 0; y < image.Height; y += value) + { + for (int x = 0; x < image.Width; x += value) + { + TPixel source = image[x, y]; + for (int pixY = y; pixY < y + value && pixY < image.Height; pixY++) + { + for (int pixX = x; pixX < x + value && pixX < image.Width; pixX++) + { + Assert.Equal(source, image[pixX, pixY]); + } + } + } + } + } + } + + [Theory] + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] + public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Pixelate(value, bounds)); + image.DebugSave(provider, value); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + int tx = x; + int ty = y; + TPixel sourceColor = source[tx, ty]; + if (bounds.Contains(tx, ty)) + { + int sourceX = tx - ((tx - bounds.Left) % value) + (value / 2); + int sourceY = ty - ((ty - bounds.Top) % value) + (value / 2); + + sourceColor = image[sourceX, sourceY]; + } + Assert.Equal(sourceColor, image[tx, ty]); + } + } + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs new file mode 100644 index 0000000000..e373230a73 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BlackWhiteTest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class BlackWhiteTest + { + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplyBlackWhiteFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(ctx => ctx.BlackWhite()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs new file mode 100644 index 0000000000..783e55d832 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/BrightnessTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + [GroupOutput("Filters")] + public class BrightnessTest + { + public static readonly TheoryData BrightnessValues + = new TheoryData + { + .5F, + 1.5F + }; + + [Theory] + [WithTestPatternImages(nameof(BrightnessValues), 48, 48, PixelTypes.Rgba32)] + public void ApplyBrightnessFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(ctx => ctx.Brightness(value), value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs new file mode 100644 index 0000000000..c0df24d291 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class ColorBlindnessTest + { + public static readonly TheoryData ColorBlindnessFilters + = new TheoryData + { + ColorBlindness.Achromatomaly, + ColorBlindness.Achromatopsia, + ColorBlindness.Deuteranomaly, + ColorBlindness.Deuteranopia, + ColorBlindness.Protanomaly, + ColorBlindness.Protanopia, + ColorBlindness.Tritanomaly, + ColorBlindness.Tritanopia + }; + + [Theory] + [WithTestPatternImages(nameof(ColorBlindnessFilters), 48, 48, PixelTypes.Rgba32)] + public void ApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs new file mode 100644 index 0000000000..b532649b3d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/ContrastTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + [GroupOutput("Filters")] + public class ContrastTest + { + public static readonly TheoryData ContrastValues + = new TheoryData + { + .5F, + 1.5F + }; + + [Theory] + [WithTestPatternImages(nameof(ContrastValues), 48, 48, PixelTypes.Rgba32)] + public void ApplyContrastFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Contrast(value), value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs new file mode 100644 index 0000000000..515b970aee --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class FilterTest + { + // Testing the generic FilterProcessor with more than one pixel type intentionally. + // There is no need to do this with the specialized ones. + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void ApplyFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + Matrix4x4 m = CreateCombinedTestFilterMatrix(); + + provider.RunValidatingProcessorTest(x => x.Filter(m)); + } + + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplyFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + Matrix4x4 m = CreateCombinedTestFilterMatrix(); + + provider.RunRectangleConstrainedValidatingProcessorTest((x, b) => x.Filter(m, b)); + } + + private static Matrix4x4 CreateCombinedTestFilterMatrix() + { + Matrix4x4 brightness = MatrixFilters.CreateBrightnessFilter(0.9F); + Matrix4x4 hue = MatrixFilters.CreateHueFilter(180F); + Matrix4x4 saturation = MatrixFilters.CreateSaturateFilter(1.5F); + Matrix4x4 m = brightness * hue * saturation; + return m; + } + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs new file mode 100644 index 0000000000..f61c529a38 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/GrayscaleTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class GrayscaleTest + { + public static readonly TheoryData GrayscaleModeTypes + = new TheoryData + { + GrayscaleMode.Bt601, + GrayscaleMode.Bt709 + }; + + /// + /// Use test patterns over loaded images to save decode time. + /// + [Theory] + [WithTestPatternImages(nameof(GrayscaleModeTypes), 48, 48, PixelTypes.Rgba32)] + public void ApplyGrayscaleFilter(TestImageProvider provider, GrayscaleMode value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Grayscale(value), value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs new file mode 100644 index 0000000000..fe9f9d5db2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/HueTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class HueTest + { + public static readonly TheoryData HueValues + = new TheoryData + { + 180, + -180 + }; + + [Theory] + [WithTestPatternImages(nameof(HueValues), 48, 48, PixelTypes.Rgba32)] + public void ApplyHueFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Hue(value), value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs new file mode 100644 index 0000000000..452397c5eb --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/InvertTest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + [GroupOutput("Filters")] + public class InvertTest + { + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplyInvertFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Invert()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs new file mode 100644 index 0000000000..a5165250b7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/KodachromeTest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class KodachromeTest + { + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplyKodachromeFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Kodachrome()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs new file mode 100644 index 0000000000..09c8becec3 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LomographTest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class LomographTest + { + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplyLomographFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Lomograph()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs new file mode 100644 index 0000000000..2cba4fb8ed --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/OpacityTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + [GroupOutput("Filters")] + public class OpacityTest + { + public static readonly TheoryData AlphaValues + = new TheoryData + { + 20/100F, + 80/100F + }; + + [Theory] + [WithTestPatternImages(nameof(AlphaValues), 48, 48, PixelTypes.Rgba32)] + public void ApplyAlphaFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Opacity(value), value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs new file mode 100644 index 0000000000..4e0347d2a4 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/PolaroidTest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class PolaroidTest + { + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplyPolaroidFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Polaroid()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs new file mode 100644 index 0000000000..9b963b94a4 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/SaturateTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class SaturateTest + { + public static readonly TheoryData SaturationValues + = new TheoryData + { + .5F, + 1.5F, + }; + + [Theory] + [WithTestPatternImages(nameof(SaturationValues), 48, 48, PixelTypes.Rgba32)] + public void ApplySaturationFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Saturate(value), value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs new file mode 100644 index 0000000000..2a63e992d7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/SepiaTest.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters +{ + [GroupOutput("Filters")] + public class SepiaTest + { + [Theory] + [WithTestPatternImages(48, 48, PixelTypes.Rgba32)] + public void ApplySepiaFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest(x => x.Sepia()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs new file mode 100644 index 0000000000..5693b6d755 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays +{ + public class GlowTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Glow()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Glow(NamedColors.Orange)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Glow(image.Width / 4F)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Glow(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs new file mode 100644 index 0000000000..0d9c3e89b3 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays +{ + public class VignetteTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Vignette()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Vignette(NamedColors.Orange)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Vignette(image.Width / 4F, image.Height / 4F)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Vignette(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs new file mode 100644 index 0000000000..161ac15e33 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class AutoOrientTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData OrientationValues + = new TheoryData + { + { RotateType.None, FlipType.None, 0 }, + { RotateType.None, FlipType.None, 1 }, + { RotateType.None, FlipType.Horizontal, 2 }, + { RotateType.Rotate180, FlipType.None, 3 }, + { RotateType.Rotate180, FlipType.Horizontal, 4 }, + { RotateType.Rotate90, FlipType.Horizontal, 5 }, + { RotateType.Rotate270, FlipType.None, 6 }, + { RotateType.Rotate90, FlipType.Vertical, 7 }, + { RotateType.Rotate90, FlipType.None, 8 }, + }; + + public static readonly TheoryData InvalidOrientationValues + = new TheoryData + { + { ExifDataType.Byte, new byte[] { 1 } }, + { ExifDataType.SignedByte, new byte[] { 2 } }, + { ExifDataType.SignedShort, BitConverter.GetBytes((short) 3) }, + { ExifDataType.Long, BitConverter.GetBytes((uint) 4) }, + { ExifDataType.SignedLong, BitConverter.GetBytes((int) 5) } + }; + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)] + public void ImageShouldAutoRotate(TestImageProvider provider, RotateType rotateType, FlipType flipType, ushort orientation) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.MetaData.ExifProfile = new ExifProfile(); + image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); + + image.Mutate(x => x.RotateFlip(rotateType, flipType)); + image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before")); + + image.Mutate(x => x.AutoOrient()); + image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "2_after")); + } + } + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(InvalidOrientationValues), DefaultPixelType)] + public void ImageShouldAutoRotateInvalidValues(TestImageProvider provider, ExifDataType dataType, byte[] orientation) + where TPixel : struct, IPixel + { + var profile = new ExifProfile(); + profile.SetValue(ExifTag.JPEGTables, orientation); + + byte[] bytes = profile.ToByteArray(); + // Change the tag into ExifTag.Orientation + bytes[16] = 18; + bytes[17] = 1; + // Change the data type + bytes[18] = (byte)dataType; + // Change the number of components + bytes[20] = 1; + + using (Image image = provider.GetImage()) + { + image.MetaData.ExifProfile = new ExifProfile(bytes); + image.Mutate(x=>x.AutoOrient()); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs new file mode 100644 index 0000000000..aa18feac2d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class CropTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldCrop(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Crop(image.Width / 2, image.Height / 2)); + image.DebugSave(provider); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs new file mode 100644 index 0000000000..57341560e3 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class EntropyCropTest : FileTestBase + { + public static readonly TheoryData EntropyCropValues + = new TheoryData + { + .25F, + .75F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(EntropyCropValues), DefaultPixelType)] + public void ImageShouldEntropyCrop(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.EntropyCrop(value)); + image.DebugSave(provider, value); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs new file mode 100644 index 0000000000..d4de4c3d2e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class FlipTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData FlipValues + = new TheoryData + { + { FlipType.None }, + { FlipType.Vertical }, + { FlipType.Horizontal }, + }; + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(FlipValues), DefaultPixelType)] + public void ImageShouldFlip(TestImageProvider provider, FlipType flipType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Flip(flipType)); + image.DebugSave(provider, flipType, Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs new file mode 100644 index 0000000000..b5f26b4883 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class PadTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldPad(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50)); + image.DebugSave(provider); + + // Check pixels are empty + for (int y = 0; y < 25; y++) + { + for (int x = 0; x < 25; x++) + { + Assert.Equal(default(TPixel), image[x, y]); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs similarity index 63% rename from tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs rename to tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs index a300672e85..98dbbadaba 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs @@ -1,20 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using System; - using System.IO; - using System.Text; - - using ImageSharp.Processing; - using ImageSharp.Processing.Processors; - - using Xunit; - using Xunit.Abstractions; +using System; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ public class ResizeProfilingBenchmarks : MeasureFixture { public ResizeProfilingBenchmarks(ITestOutputHelper output) @@ -34,7 +30,7 @@ namespace ImageSharp.Tests.Processing.Transforms { using (var image = new Image(width, height)) { - image.Resize(width / 4, height / 4); + image.Mutate(x => x.Resize(width / 4, height / 4)); } }); } @@ -42,13 +38,13 @@ namespace ImageSharp.Tests.Processing.Transforms // [Fact] public void PrintWeightsData() { - var proc = new ResizeProcessor(new BicubicResampler(), 200, 200); + var proc = new ResizeProcessor(KnownResamplers.Bicubic, 200, 200); - ResamplingWeightedProcessor.WeightsBuffer weights = proc.PrecomputeWeights(200, 500); + WeightsBuffer weights = proc.PrecomputeWeights(200, 500); var bld = new StringBuilder(); - foreach (ResamplingWeightedProcessor.WeightsWindow window in weights.Weights) + foreach (WeightsWindow window in weights.Weights) { Span span = window.GetWindowSpan(); for (int i = 0; i < window.Length; i++) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs new file mode 100644 index 0000000000..8370a802cd --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -0,0 +1,351 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using SixLabors.Primitives; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class ResizeTests : FileTestBase + { + public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; + + public static readonly TheoryData AllReSamplers = + new TheoryData + { + { "Bicubic", KnownResamplers.Bicubic }, + { "Triangle", KnownResamplers.Triangle}, + { "NearestNeighbor", KnownResamplers.NearestNeighbor }, + { "Box", KnownResamplers.Box }, + // { "Lanczos2", KnownResamplers.Lanczos2 }, TODO: Add expected file + { "Lanczos3", KnownResamplers.Lanczos3 }, + { "Lanczos5", KnownResamplers.Lanczos5 }, + { "MitchellNetravali", KnownResamplers.MitchellNetravali }, + { "Lanczos8", KnownResamplers.Lanczos8 }, + { "Hermite", KnownResamplers.Hermite }, + { "Spline", KnownResamplers.Spline }, + { "Robidoux", KnownResamplers.Robidoux }, + { "RobidouxSharp", KnownResamplers.RobidouxSharp }, + { "Welch", KnownResamplers.Welch } + }; + + [Theory] + [WithTestPatternImages(nameof(AllReSamplers), 100, 100, DefaultPixelType, 0.5f)] + [WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.5f)] + [WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.3f)] + public void Resize_WorksWithAllResamplers(TestImageProvider provider, string name, IResampler sampler, float ratio) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + SizeF newSize = image.Size() * ratio; + image.Mutate(x => x.Resize((Size)newSize, sampler, false)); + string details = $"{name}-{ratio.ToString(System.Globalization.CultureInfo.InvariantCulture)}"; + + image.DebugSave(provider, details); + image.CompareToReferenceOutput(provider, details); + } + } + + [Theory] + [WithTestPatternImages(100, 100, DefaultPixelType)] + public void Resize_Compand(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Size() / 2, true)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithTestPatternImages(50, 50, CommonNonDefaultPixelTypes)] + public void Resize_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, true)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Kaboom, DefaultPixelType)] + public void Resize_DoesNotBleedAlphaPixels(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + public void Resize_IsAppliedToAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, KnownResamplers.NearestNeighbor)); + + // Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :( + image.DebugSave(provider, extension: Extensions.Gif); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeFromSourceRectangle(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); + var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Resize(image.Width, image.Height, KnownResamplers.Bicubic, sourceRectangle, destRectangle, false)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWidthAndKeepAspect(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 3, 0, false)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeHeightAndKeepAspect(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(0, image.Height / 3, false)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithCropWidthMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width / 2, image.Height) + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithCropHeightMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width, image.Height / 2) + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithPadMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width + 200, image.Height), + Mode = ResizeMode.Pad + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithBoxPadMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width + 200, image.Height + 200), + Mode = ResizeMode.BoxPad + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithMaxMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(300, 300), + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithMinMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)), + Mode = ResizeMode.Min + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithStretchMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width / 2, image.Height), + Mode = ResizeMode.Stretch + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void BicubicWindowOscillatesCorrectly(float x, float expected) + { + var sampler = KnownResamplers.Bicubic; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void TriangleWindowOscillatesCorrectly(float x, float expected) + { + var sampler = KnownResamplers.Triangle; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) + { + var sampler = KnownResamplers.Lanczos3; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-4, 0)] + [InlineData(-2, 0)] + [InlineData(0, 1)] + [InlineData(2, 0)] + [InlineData(4, 0)] + public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) + { + var sampler = KnownResamplers.Lanczos5; + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs new file mode 100644 index 0000000000..e7415e1619 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class RotateFlipTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData RotateFlipValues + = new TheoryData + { + { RotateType.None, FlipType.Vertical }, + { RotateType.None, FlipType.Horizontal }, + { RotateType.Rotate90, FlipType.None }, + { RotateType.Rotate180, FlipType.None }, + { RotateType.Rotate270, FlipType.None }, + }; + + [Theory] + [WithTestPatternImages(nameof(RotateFlipValues), 100, 50, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateFlipValues), 50, 100, DefaultPixelType)] + public void RotateFlip(TestImageProvider provider, RotateType rotateType, FlipType flipType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.RotateFlip(rotateType, flipType)); + image.DebugSave(provider, string.Join("_", rotateType, flipType)); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs new file mode 100644 index 0000000000..b5220ea948 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + using System; + using System.Reflection; + + public class RotateTests : FileTestBase + { + public static readonly TheoryData RotateAngles + = new TheoryData + { + 50, -50, 170, -170 + }; + + public static readonly TheoryData RotateEnumValues + = new TheoryData + { + RotateType.None, + RotateType.Rotate90, + RotateType.Rotate180, + RotateType.Rotate270 + }; + + [Theory] + [WithTestPatternImages(nameof(RotateAngles), 100, 50, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateAngles), 50, 100, DefaultPixelType)] + public void Rotate_WithAngle(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Rotate(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithTestPatternImages(nameof(RotateEnumValues), 100, 50, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateEnumValues), 50, 100, DefaultPixelType)] + public void Rotate_WithRotateTypeEnum(TestImageProvider provider, RotateType value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Rotate(value)); + image.DebugSave(provider, value); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs new file mode 100644 index 0000000000..17bf3def26 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + using System; + using System.Collections.Generic; + using System.Reflection; + + using SixLabors.ImageSharp.Processing; + + public class SkewTest : FileTestBase + { + public static readonly TheoryData SkewValues + = new TheoryData + { + { 20, 10 }, + { -20, -10 } + }; + + public static readonly List ResamplerNames + = new List + { + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Box), + nameof(KnownResamplers.CatmullRom), + nameof(KnownResamplers.Hermite), + nameof(KnownResamplers.Lanczos2), + nameof(KnownResamplers.Lanczos3), + nameof(KnownResamplers.Lanczos5), + nameof(KnownResamplers.Lanczos8), + nameof(KnownResamplers.MitchellNetravali), + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Robidoux), + nameof(KnownResamplers.RobidouxSharp), + nameof(KnownResamplers.Spline), + nameof(KnownResamplers.Triangle), + nameof(KnownResamplers.Welch), + }; + + [Theory] + [WithTestPatternImages(nameof(SkewValues), 100, 50, DefaultPixelType)] + public void ImageShouldSkew(TestImageProvider provider, float x, float y) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(i => i.Skew(x, y)); + image.DebugSave(provider, string.Join("_", x, y)); + } + } + + [Theory] + [WithTestPatternImages(nameof(SkewValues), 100, 50, DefaultPixelType)] + public void ImageShouldSkewWithSampler(TestImageProvider provider, float x, float y) + where TPixel : struct, IPixel + { + foreach (string resamplerName in ResamplerNames) + { + IResampler sampler = GetResampler(resamplerName); + using (Image image = provider.GetImage()) + { + image.Mutate(i => i.Skew(x, y, sampler)); + image.DebugSave(provider, string.Join("_", x, y, resamplerName)); + } + } + } + + private static IResampler GetResampler(string name) + { + PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); + + if (property == null) + { + throw new Exception("Invalid property name!"); + } + + return (IResampler)property.GetValue(null); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs new file mode 100644 index 0000000000..ace6cb70d6 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs @@ -0,0 +1,258 @@ +using System; +using System.Numerics; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using Xunit; +using Xunit.Abstractions; +using SixLabors.ImageSharp.Helpers; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class AffineTransformTests + { + private readonly ITestOutputHelper Output; + + /// + /// angleDeg, sx, sy, tx, ty + /// + public static readonly TheoryData TransformValues + = new TheoryData + { + { 0, 1, 1, 0, 0 }, + { 50, 1, 1, 0, 0 }, + { 0, 1, 1, 20, 10 }, + { 50, 1, 1, 20, 10 }, + { 0, 1, 1, -20, -10 }, + { 50, 1, 1, -20, -10 }, + { 50, 1.5f, 1.5f, 0, 0 }, + { 50, 1.1F, 1.3F, 30, -20 }, + { 0, 2f, 1f, 0, 0 }, + { 0, 1f, 2f, 0, 0 }, + }; + + public static readonly TheoryData ResamplerNames = + new TheoryData + { + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Box), + nameof(KnownResamplers.CatmullRom), + nameof(KnownResamplers.Hermite), + nameof(KnownResamplers.Lanczos2), + nameof(KnownResamplers.Lanczos3), + nameof(KnownResamplers.Lanczos5), + nameof(KnownResamplers.Lanczos8), + nameof(KnownResamplers.MitchellNetravali), + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Robidoux), + nameof(KnownResamplers.RobidouxSharp), + nameof(KnownResamplers.Spline), + nameof(KnownResamplers.Triangle), + nameof(KnownResamplers.Welch), + }; + + public static readonly TheoryData Transform_DoesNotCreateEdgeArtifacts_ResamplerNames = + new TheoryData + { + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Triangle), + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Lanczos8), + }; + + public AffineTransformTests(ITestOutputHelper output) + { + this.Output = output; + } + + /// + /// The output of an "all white" image should be "all white" or transparent, regardless of the transformation and the resampler. + /// + [Theory] + [WithSolidFilledImages(nameof(Transform_DoesNotCreateEdgeArtifacts_ResamplerNames), 5, 5, 255, 255, 255, 255, PixelTypes.Rgba32)] + public void Transform_DoesNotCreateEdgeArtifacts(TestImageProvider provider, string resamplerName) + where TPixel : struct, IPixel + { + IResampler resampler = GetResampler(resamplerName); + using (Image image = provider.GetImage()) + { + var rotate = Matrix3x2.CreateRotation((float)Math.PI / 4F, new Vector2(5 / 2F, 5 / 2F)); + var translate = Matrix3x2.CreateTranslation((7 - 5) / 2F, (7 - 5) / 2F); + + Rectangle sourceRectangle = image.Bounds(); + Matrix3x2 matrix = rotate * translate; + + Rectangle destRectangle = TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix); + + image.Mutate(c => c.Transform(matrix, resampler, destRectangle)); + image.DebugSave(provider, resamplerName); + + VerifyAllPixelsAreWhiteOrTransparent(image); + } + } + + [Theory] + [WithTestPatternImages(nameof(TransformValues), 100, 50, PixelTypes.Rgba32)] + public void Transform_RotateScaleTranslate( + TestImageProvider provider, + float angleDeg, + float sx, float sy, + float tx, float ty) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(angleDeg); + var translate = Matrix3x2.CreateTranslation(tx, ty); + var scale = Matrix3x2.CreateScale(sx, sy); + Matrix3x2 m = rotate * scale * translate; + + this.PrintMatrix(m); + + image.Mutate(i => i.Transform(m, KnownResamplers.Bicubic)); + + string testOutputDetails = $"R({angleDeg})_S({sx},{sy})_T({tx},{ty})"; + image.DebugSave(provider, testOutputDetails); + image.CompareToReferenceOutput(provider, testOutputDetails); + } + } + + [Theory] + [WithTestPatternImages(96, 96, PixelTypes.Rgba32, 50, 0.8f)] + public void Transform_RotateScale_ManuallyCentered(TestImageProvider provider, float angleDeg, float s) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Matrix3x2 m = this.MakeManuallyCenteredMatrix(angleDeg, s, image); + + image.Mutate(i => i.Transform(m, KnownResamplers.Bicubic)); + + string testOutputDetails = $"R({angleDeg})_S({s})"; + image.DebugSave(provider, testOutputDetails); + image.CompareToReferenceOutput(provider, testOutputDetails); + } + } + + public static readonly TheoryData Transform_IntoRectangle_Data = + new TheoryData + { + { 0, 0, 10, 10 }, + { 0, 0, 5, 10 }, + { 0, 0, 10, 5 }, + { 5, 0, 5, 10 }, + {-5,-5, 20, 20 } + }; + + /// + /// Testing transforms using custom source rectangles: + /// https://github.com/SixLabors/ImageSharp/pull/386#issuecomment-357104963 + /// + [Theory] + [WithTestPatternImages(96, 48, PixelTypes.Rgba32)] + public void Transform_FromSourceRectangle1(TestImageProvider provider) + where TPixel : struct, IPixel + { + var rectangle = new Rectangle(48, 0, 96, 36); + + using (Image image = provider.GetImage()) + { + var m = Matrix3x2.CreateScale(2.0F, 1.5F); + + image.Mutate(i => i.Transform(m, KnownResamplers.Spline, rectangle)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithTestPatternImages(96, 48, PixelTypes.Rgba32)] + public void Transform_FromSourceRectangle2(TestImageProvider provider) + where TPixel : struct, IPixel + { + var rectangle = new Rectangle(0, 24, 48, 48); + + using (Image image = provider.GetImage()) + { + var m = Matrix3x2.CreateScale(1.0F, 2.0F); + + image.Mutate(i => i.Transform(m, KnownResamplers.Spline, rectangle)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithTestPatternImages(nameof(ResamplerNames), 150, 150, PixelTypes.Rgba32)] + public void Transform_WithSampler(TestImageProvider provider, string resamplerName) + where TPixel : struct, IPixel + { + IResampler sampler = GetResampler(resamplerName); + using (Image image = provider.GetImage()) + { + Matrix3x2 m = this.MakeManuallyCenteredMatrix(50, 0.6f, image); + + image.Mutate(i => + { + i.Transform(m, sampler); + }); + + image.DebugSave(provider, resamplerName); + image.CompareToReferenceOutput(provider, resamplerName); + } + } + + private Matrix3x2 MakeManuallyCenteredMatrix(float angleDeg, float s, Image image) + where TPixel : struct, IPixel + { + Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(angleDeg); + Vector2 toCenter = 0.5f * new Vector2(image.Width, image.Height); + var translate = Matrix3x2.CreateTranslation(-toCenter); + var translateBack = Matrix3x2.CreateTranslation(toCenter); + var scale = Matrix3x2.CreateScale(s); + + Matrix3x2 m = translate * rotate * scale * translateBack; + + this.PrintMatrix(m); + return m; + } + + private static IResampler GetResampler(string name) + { + PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); + + if (property == null) + { + throw new Exception("Invalid property name!"); + } + + return (IResampler)property.GetValue(null); + } + + private static void VerifyAllPixelsAreWhiteOrTransparent(Image image) + where TPixel : struct, IPixel + { + TPixel[] data = new TPixel[image.Width * image.Height]; + image.Frames.RootFrame.SavePixelData(data); + var rgba = default(Rgba32); + var white = new Rgb24(255, 255, 255); + foreach (TPixel pixel in data) + { + pixel.ToRgba32(ref rgba); + if (rgba.A == 0) continue; + + Assert.Equal(white, rgba.Rgb); + } + } + + private void PrintMatrix(Matrix3x2 a) + { + string s = $"{a.M11:F10},{a.M12:F10},{a.M21:F10},{a.M22:F10},{a.M31:F10},{a.M32:F10}"; + this.Output.WriteLine(s); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs index 7bc0c8bb52..20de25054c 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs @@ -1,48 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class AutoOrientTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class AutoOrientTests : BaseImageOperationsExtensionTest { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData OrientationValues - = new TheoryData + [Fact] + public void AutoOrient_AutoOrientProcessor() { - { RotateType.None, FlipType.None, 0 }, - { RotateType.None, FlipType.None, 1 }, - { RotateType.None, FlipType.Horizontal, 2 }, - { RotateType.Rotate180, FlipType.None, 3 }, - { RotateType.Rotate180, FlipType.Horizontal, 4 }, - { RotateType.Rotate90, FlipType.Horizontal, 5 }, - { RotateType.Rotate270, FlipType.None, 6 }, - { RotateType.Rotate90, FlipType.Vertical, 7 }, - { RotateType.Rotate90, FlipType.None, 8 }, - }; - - [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)] - public void ImageShouldAutoRotate(TestImageProvider provider, RotateType rotateType, FlipType flipType, ushort orientation) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.MetaData.ExifProfile = new ExifProfile(); - image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); - - image.RotateFlip(rotateType, flipType) - .DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before"), Extensions.Bmp) - .AutoOrient() - .DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "2_after"), Extensions.Bmp); - } + this.operations.AutoOrient(); + this.Verify>(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index ca20abf790..a001efc417 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -1,26 +1,37 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms { - using ImageSharp.PixelFormats; + public class CropTest : BaseImageOperationsExtensionTest + { + [Theory] + [InlineData(10, 10)] + [InlineData(12, 123)] + public void Crop_Width_height_CropProcessorWithRectangleSet(int width, int height) + { + this.operations.Crop(width, height); + var processor = this.Verify>(); - using Xunit; + Assert.Equal(new Rectangle(0, 0, width, height), processor.CropRectangle); + } - public class CropTest : FileTestBase - { [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldCrop(TestImageProvider provider) - where TPixel : struct, IPixel + [InlineData(10, 10, 2, 6)] + [InlineData(12, 123, 6, 2)] + public void Crop_Rectangle_CropProcessorWithRectangleSet(int x, int y, int width, int height) { - using (Image image = provider.GetImage()) - { - image.Crop(image.Width / 2, image.Height / 2) - .DebugSave(provider, null, Extensions.Bmp); - } + var rect = new Rectangle(x, y, width, height); + this.operations.Crop(rect); + var processor = this.Verify>(); + + Assert.Equal(rect, processor.CropRectangle); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs index 24febd5b2f..c1cde48794 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs @@ -1,33 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class EntropyCropTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class EntropyCropTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData EntropyCropValues - = new TheoryData - { - .25F, - .75F - }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(EntropyCropValues), DefaultPixelType)] - public void ImageShouldEntropyCrop(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [InlineData(0.5f)] + [InlineData(.2f)] + public void EntropyCrop_threasholdFloat_EntropyCropProcessorWithThreshold(float threashold) { - using (Image image = provider.GetImage()) - { - image.EntropyCrop(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.EntropyCrop(threashold); + var processor = this.Verify>(); + + Assert.Equal(threashold, processor.Threshold); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index 45ab1e5f8b..4b3e97d106 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -1,37 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class FlipTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class FlipTests : BaseImageOperationsExtensionTest { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData FlipValues - = new TheoryData - { - { FlipType.None }, - { FlipType.Vertical }, - { FlipType.Horizontal }, - }; [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(FlipValues), DefaultPixelType)] - public void ImageShouldFlip(TestImageProvider provider, FlipType flipType) - where TPixel : struct, IPixel + [InlineData(FlipType.None)] + [InlineData(FlipType.Horizontal)] + [InlineData(FlipType.Vertical)] + public void Flip_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(FlipType flip) { - using (Image image = provider.GetImage()) - { - image.Flip(flipType) - .DebugSave(provider, flipType, Extensions.Bmp); - } + this.operations.Flip(flip); + var flipProcessor = this.Verify>(); + + Assert.Equal(flip, flipProcessor.FlipType); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 7caa1e7c0e..58fc7fa802 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,35 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; - public class PadTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class PadTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldPad(TestImageProvider provider) - where TPixel : struct, IPixel +#pragma warning disable xUnit1004 // Test methods should not be skipped + [Fact(Skip = "Skip this is a helper around resize, skip until resize can be refactord")] +#pragma warning restore xUnit1004 // Test methods should not be skipped + public void Pad_width_height_ResizeProcessorWithCorrectOPtionsSet() { - using (Image image = provider.GetImage()) - { - image.Pad(image.Width + 50, image.Height + 50) - .DebugSave(provider, null, Extensions.Bmp); - - // Check pixels are empty - for (int y = 0; y < 25; y++) - { - for (int x = 0; x < 25; x++) - { - Assert.Equal(default(TPixel), image[x, y]); - } - } - } + throw new NotImplementedException("Write test here"); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index fb195254e3..d2f5cb2c3d 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -1,273 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using Xunit; - public class ResizeTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class ResizeTests : BaseImageOperationsExtensionTest { - public static readonly string[] ResizeFiles = { TestImages.Jpeg.Baseline.Calliphora }; - - public static readonly TheoryData ReSamplers = - new TheoryData - { - { "Bicubic", new BicubicResampler() }, - { "Triangle", new TriangleResampler() }, - { "NearestNeighbor", new NearestNeighborResampler() }, - { "Box", new BoxResampler() }, - { "Lanczos3", new Lanczos3Resampler() }, - { "Lanczos5", new Lanczos5Resampler() }, - { "MitchellNetravali", new MitchellNetravaliResampler() }, - { "Lanczos8", new Lanczos8Resampler() }, - { "Hermite", new HermiteResampler() }, - { "Spline", new SplineResampler() }, - { "Robidoux", new RobidouxResampler() }, - { "RobidouxSharp", new RobidouxSharpResampler() }, - { "Welch", new WelchResampler() } - }; - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResize(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Resize(image.Width / 2, image.Height / 2, sampler, true) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeFromSourceRectangle(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); - var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - - image.Resize(image.Width, image.Height, sampler, sourceRectangle, destRectangle, false) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWidthAndKeepAspect(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Resize(image.Width / 3, 0, sampler, false) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeHeightAndKeepAspect(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Resize(0, image.Height / 3, sampler, false) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithCropWidthMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width / 2, image.Height) - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithCropHeightMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width, image.Height / 2) - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithPadMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width + 200, image.Height), - Mode = ResizeMode.Pad - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithBoxPadMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel +#pragma warning disable xUnit1004 // Test methods should not be skipped + [Fact(Skip = "Skip resize tests as they need refactoring to be simpler and just pass data into the resize processor.")] +#pragma warning restore xUnit1004 // Test methods should not be skipped + public void TestMissing() { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width + 200, image.Height + 200), - Mode = ResizeMode.BoxPad - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithMaxMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(300, 300), - Mode = ResizeMode.Max - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithMinMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size((int)MathF.Round(image.Width * .75F), (int)MathF.Round(image.Height * .95F)), - Mode = ResizeMode.Min - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithStretchMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width / 2, image.Height), - Mode = ResizeMode.Stretch - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void BicubicWindowOscillatesCorrectly(float x, float expected) - { - var sampler = new BicubicResampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void TriangleWindowOscillatesCorrectly(float x, float expected) - { - var sampler = new TriangleResampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) - { - var sampler = new Lanczos3Resampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-4, 0)] - [InlineData(-2, 0)] - [InlineData(0, 1)] - [InlineData(2, 0)] - [InlineData(4, 0)] - public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) - { - var sampler = new Lanczos5Resampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); + // + throw new NotImplementedException("Write test here"); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs index f85ef6f13a..75d7067702 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs @@ -1,39 +1,38 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class RotateFlipTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class RotateFlipTests : BaseImageOperationsExtensionTest { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData RotateFlipValues - = new TheoryData - { - { RotateType.None, FlipType.Vertical }, - { RotateType.None, FlipType.Horizontal }, - { RotateType.Rotate90, FlipType.None }, - { RotateType.Rotate180, FlipType.None }, - { RotateType.Rotate270, FlipType.None }, - }; [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(RotateFlipValues), DefaultPixelType)] - public void ImageShouldRotateFlip(TestImageProvider provider, RotateType rotateType, FlipType flipType) - where TPixel : struct, IPixel + [InlineData(RotateType.None, FlipType.None, 0)] + [InlineData(RotateType.Rotate90, FlipType.None, 90)] + [InlineData(RotateType.Rotate180, FlipType.None, 180)] + [InlineData(RotateType.Rotate270, FlipType.None, 270)] + [InlineData(RotateType.None, FlipType.Horizontal, 0)] + [InlineData(RotateType.Rotate90, FlipType.Horizontal, 90)] + [InlineData(RotateType.Rotate180, FlipType.Horizontal, 180)] + [InlineData(RotateType.Rotate270, FlipType.Horizontal, 270)] + [InlineData(RotateType.None, FlipType.Vertical, 0)] + [InlineData(RotateType.Rotate90, FlipType.Vertical, 90)] + [InlineData(RotateType.Rotate180, FlipType.Vertical, 180)] + [InlineData(RotateType.Rotate270, FlipType.Vertical, 270)] + public void Rotate_degreesFloat_RotateProcessorWithAnglesSetrue(RotateType angle, FlipType flip, float expectedAngle) { - using (Image image = provider.GetImage()) - { - image.RotateFlip(rotateType, flipType) - .DebugSave(provider, string.Join("_", rotateType, flipType), Extensions.Bmp); - } + this.operations.RotateFlip(angle, flip); + RotateProcessor rotateProcessor = this.Verify>(0); + FlipProcessor flipProcessor = this.Verify>(1); + + Assert.Equal(expectedAngle, rotateProcessor.Degrees); + Assert.Equal(flip, flipProcessor.FlipType); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs index 1f18564290..a990fa88ca 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs @@ -1,55 +1,36 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class RotateTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class RotateTests : BaseImageOperationsExtensionTest { - public static readonly TheoryData RotateFloatValues - = new TheoryData - { - 170, - -170 - }; - - public static readonly TheoryData RotateEnumValues - = new TheoryData - { - RotateType.None, - RotateType.Rotate90, - RotateType.Rotate180, - RotateType.Rotate270 - }; - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(RotateFloatValues), DefaultPixelType)] - public void ImageShouldRotate(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [InlineData(85.6f)] + [InlineData(21)] + public void RotateDegreesFloatRotateProcessorWithAnglesSet(float angle) { - using (Image image = provider.GetImage()) - { - image.Rotate(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.Rotate(angle); + RotateProcessor processor = this.Verify>(); + + Assert.Equal(angle, processor.Degrees); } [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(RotateEnumValues), DefaultPixelType)] - public void ImageShouldRotateEnum(TestImageProvider provider, RotateType value) - where TPixel : struct, IPixel + [InlineData(RotateType.None, 0)] + [InlineData(RotateType.Rotate90, 90)] + [InlineData(RotateType.Rotate180, 180)] + [InlineData(RotateType.Rotate270, 270)] + public void RotateRotateTypeRotateProcessorWithAnglesConvertedFromEnum(RotateType angle, float expectedangle) { - using (Image image = provider.GetImage()) - { - image.Rotate(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.Rotate(angle); // is this api needed ??? + RotateProcessor processor = this.Verify>(); + + Assert.Equal(expectedangle, processor.Degrees); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index f2c2d7cbd2..d2cc5764ed 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -1,33 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - - using Xunit; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class SkewTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class SkewTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData SkewValues - = new TheoryData + [Fact] + public void SkewXYCreateSkewProcessorWithAnglesSet() { - { 20, 10 }, - { -20, -10 } - }; + this.operations.Skew(10, 20); + SkewProcessor processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(SkewValues), DefaultPixelType)] - public void ImageShouldSkew(TestImageProvider provider, float x, float y) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Skew(x, y) - .DebugSave(provider, string.Join("_", x, y), Extensions.Bmp); - } + Assert.Equal(10, processor.DegreesX); + Assert.Equal(20, processor.DegreesY); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs new file mode 100644 index 0000000000..c5b6b1ad72 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class TransformsHelpersTest + { + [Fact] + public void HelperCanChangeExifDataType() + { + int xy = 1; + + using (var img = new Image(xy, xy)) + { + var profile = new ExifProfile(); + img.MetaData.ExifProfile = profile; + profile.SetValue(ExifTag.PixelXDimension, (uint)xy); + profile.SetValue(ExifTag.PixelYDimension, (uint)xy); + + Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); + Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); + + TransformHelpers.UpdateDimensionalMetData(img); + + Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelXDimension).DataType); + Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelYDimension).DataType); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs new file mode 100644 index 0000000000..b5a8d1265c --- /dev/null +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -0,0 +1,107 @@ +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Quantizers; + + using Xunit; + + public class QuantizedImageTests + { + [Fact] + public void QuantizersDitherByDefault() + { + var palette = new PaletteQuantizer(); + var octree = new OctreeQuantizer(); + var wu = new WuQuantizer(); + + Assert.True(palette.Dither); + Assert.True(octree.Dither); + Assert.True(wu.Dither); + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] + public void PaletteQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.True(image[0, 0].Equals(default(TPixel))); + + IQuantizer quantizer = new PaletteQuantizer { Dither = dither }; + + foreach (ImageFrame frame in image.Frames) + { + QuantizedImage quantized = quantizer.Quantize(frame, 256); + + int index = this.GetTransparentIndex(quantized); + Assert.Equal(index, quantized.Pixels[0]); + } + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] + public void OctreeQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.True(image[0, 0].Equals(default(TPixel))); + + IQuantizer quantizer = new OctreeQuantizer { Dither = dither }; + + foreach (ImageFrame frame in image.Frames) + { + QuantizedImage quantized = quantizer.Quantize(frame, 256); + + int index = this.GetTransparentIndex(quantized); + Assert.Equal(index, quantized.Pixels[0]); + } + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] + public void WuQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.True(image[0, 0].Equals(default(TPixel))); + + IQuantizer quantizer = new WuQuantizer { Dither = dither }; + + foreach (ImageFrame frame in image.Frames) + { + QuantizedImage quantized = quantizer.Quantize(frame, 256); + + int index = this.GetTransparentIndex(quantized); + Assert.Equal(index, quantized.Pixels[0]); + } + } + } + + private int GetTransparentIndex(QuantizedImage quantized) + where TPixel : struct, IPixel + { + // Transparent pixels are much more likely to be found at the end of a palette + int index = -1; + var trans = default(Rgba32); + for (int i = quantized.Palette.Length - 1; i >= 0; i--) + { + quantized.Palette[i].ToRgba32(ref trans); + + if (trans.Equals(default(Rgba32))) + { + index = i; + } + } + + return index; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestBase.cs b/tests/ImageSharp.Tests/TestBase.cs deleted file mode 100644 index c7514d5aee..0000000000 --- a/tests/ImageSharp.Tests/TestBase.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - using System.Reflection; - - using ImageSharp.Formats; - - /// - /// The test base class. Inherit from this class for any image manipulation tests. - /// - public abstract class TestBase - { - /// - /// Creates the image output directory. - /// - /// The path. - /// The path parts. - /// - /// The . - /// - protected string CreateOutputDirectory(string path, params string[] pathParts) - { - string assemblyLocation = typeof(TestFile).GetTypeInfo().Assembly.Location; - assemblyLocation = Path.GetDirectoryName(assemblyLocation); - path = Path.GetFullPath(Path.Combine(assemblyLocation, "../../../TestOutput", path)); - - if (pathParts != null && pathParts.Length > 0) - { - path = Path.Combine(path, Path.Combine(pathParts)); - } - - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - return path; - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs index a14433f1e1..4fd798f345 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataArray { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs index c8d517aac6..b39892a81d 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Numerics; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.Tests +{ internal static class IccTestDataCurves { #region Response diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs index cc2dfc6e97..4ab0b0f6b0 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs @@ -1,9 +1,9 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using SixLabors.ImageSharp.MetaData.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataLut { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs index 78e493829c..8e4b7d3d5b 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Numerics; +using SixLabors.ImageSharp.Memory; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using ImageSharp.Memory; - internal static class IccTestDataMatrix { #region 2D diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs index 49e9550df5..fb648198da 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs @@ -1,9 +1,9 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using SixLabors.ImageSharp.MetaData.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataMultiProcessElement { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs index fdbcb31271..3b8c3321a6 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs @@ -1,9 +1,13 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Globalization; - using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.Tests +{ internal static class IccTestDataNonPrimitives { #region DateTime diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs index fcfa2d0d79..b24e3f24a4 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataPrimitives { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 32a4a8e57b..3cf66ffedd 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System; using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataProfiles { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs index 05942ab618..f7c4efa928 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Globalization; - using System.Numerics; +using System.Globalization; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.Tests +{ internal static class IccTestDataTagDataEntry { #region TagDataEntry Header @@ -455,15 +454,46 @@ namespace ImageSharp.Tests #region MultiLocalizedUnicodeTagDataEntry - private static readonly IccLocalizedString LocalizedString_Rand_enUs = new IccLocalizedString(new CultureInfo("en-US"), IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand_deDE = new IccLocalizedString(new CultureInfo("de-DE"), IccTestDataPrimitives.Unicode_ValRand3); - private static readonly IccLocalizedString LocalizedString_Rand2_deDE = new IccLocalizedString(new CultureInfo("de-DE"), IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand_en = new IccLocalizedString(new CultureInfo("en"), IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand_enUS = CreateLocalizedString("en", "US", IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand_deDE = CreateLocalizedString("de", "DE", IccTestDataPrimitives.Unicode_ValRand3); + private static readonly IccLocalizedString LocalizedString_Rand2_deDE = CreateLocalizedString("de", "DE", IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand2_esXL = CreateLocalizedString("es", "XL", IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand2_xyXL = CreateLocalizedString("xy", "XL", IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand_en = CreateLocalizedString("en", null, IccTestDataPrimitives.Unicode_ValRand2); private static readonly IccLocalizedString LocalizedString_Rand_Invariant = new IccLocalizedString(CultureInfo.InvariantCulture, IccTestDataPrimitives.Unicode_ValRand3); - private static readonly IccLocalizedString[] LocalizedString_RandArr_enUs_deDE = new IccLocalizedString[] + private static IccLocalizedString CreateLocalizedString(string language, string country, string text) + { + CultureInfo culture; + if (country == null) + { + try + { + culture = new CultureInfo(language); + } + catch (CultureNotFoundException) + { + culture = CultureInfo.InvariantCulture; + } + } + else + { + try + { + culture = new CultureInfo($"{language}-{country}"); + } + catch (CultureNotFoundException) + { + return CreateLocalizedString(language, null, text); + } + } + + return new IccLocalizedString(culture, text); + } + + private static readonly IccLocalizedString[] LocalizedString_RandArr_enUS_deDE = new IccLocalizedString[] { - LocalizedString_Rand_enUs, + LocalizedString_Rand_enUS, LocalizedString_Rand_deDE, }; private static readonly IccLocalizedString[] LocalizedString_RandArr_en_Invariant = new IccLocalizedString[] @@ -471,13 +501,15 @@ namespace ImageSharp.Tests LocalizedString_Rand_en, LocalizedString_Rand_Invariant, }; - private static readonly IccLocalizedString[] LocalizedString_SameArr_enUs_deDE = new IccLocalizedString[] + private static readonly IccLocalizedString[] LocalizedString_SameArr_enUS_deDE_esXL_xyXL = new IccLocalizedString[] { - LocalizedString_Rand_enUs, - LocalizedString_Rand2_deDE + LocalizedString_Rand_enUS, + LocalizedString_Rand2_deDE, + LocalizedString_Rand2_esXL, + LocalizedString_Rand2_xyXL, }; - public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_RandArr_enUs_deDE); + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_RandArr_enUS_deDE); public static readonly byte[] MultiLocalizedUnicode_Arr = ArrayHelper.Concat ( IccTestDataPrimitives.UInt32_2, @@ -530,19 +562,27 @@ namespace ImageSharp.Tests IccTestDataPrimitives.Unicode_Rand3 ); - public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val3 = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_SameArr_enUs_deDE); + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val3 = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_SameArr_enUS_deDE_esXL_xyXL); public static readonly byte[] MultiLocalizedUnicode_Arr3 = ArrayHelper.Concat ( - IccTestDataPrimitives.UInt32_2, + IccTestDataPrimitives.UInt32_4, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 new byte[] { (byte)'e', (byte)'n', (byte)'U', (byte)'S' }, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 - new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 + new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 new byte[] { (byte)'d', (byte)'e', (byte)'D', (byte)'E' }, new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 - new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 + new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 + + new byte[] { (byte)'e', (byte)'s', (byte)'X', (byte)'L' }, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 + + new byte[] { (byte)'x', (byte)'y', (byte)'X', (byte)'L' }, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + new byte[] { 0x00, 0x00, 0x00, 0x40 }, // 64 IccTestDataPrimitives.Unicode_Rand2 ); @@ -670,8 +710,8 @@ namespace ImageSharp.Tests ( new IccProfileSequenceIdentifier[] { - new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUs_deDE), - new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUs_deDE), + new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUS_deDE), + new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUS_deDE), } ); public static readonly byte[] ProfileSequenceIdentifier_Arr = ArrayHelper.Concat diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index c0f9deebb3..b736dce207 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -1,19 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - /// /// A test image file. /// @@ -25,19 +24,20 @@ namespace ImageSharp.Tests private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); /// - /// The formats directory. + /// The "Formats" directory, as lazy value /// - private static readonly string FormatsDirectory = GetFormatsDirectory(); - + // ReSharper disable once InconsistentNaming + private static readonly Lazy inputImagesDirectory = new Lazy(() => TestEnvironment.InputImagesDirectoryFullPath); + /// - /// The image. + /// The image (lazy initialized value) /// - private readonly Image image; + private Image image; /// - /// The file. + /// The image bytes /// - private readonly string file; + private byte[] bytes; /// /// Initializes a new instance of the class. @@ -45,34 +45,40 @@ namespace ImageSharp.Tests /// The file. private TestFile(string file) { - this.file = file; - - this.Bytes = File.ReadAllBytes(file); - this.image = Image.Load(this.Bytes); + this.FullPath = file; } /// - /// Gets the bytes. + /// Gets the image bytes. /// - public byte[] Bytes { get; } + public byte[] Bytes => this.bytes ?? (this.bytes = File.ReadAllBytes(this.FullPath)); /// - /// The file name. + /// The full path to file. /// - public string FilePath => this.file; + public string FullPath { get; } /// /// The file name. /// - public string FileName => Path.GetFileName(this.file); + public string FileName => Path.GetFileName(this.FullPath); /// /// The file name without extension. /// - public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.file); + public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.FullPath); + + /// + /// Gets the image with lazy initialization. + /// + private Image Image => this.image ?? (this.image = ImageSharp.Image.Load(this.Bytes)); /// - /// Gets the full qualified path to the file. + /// + private static string InputImagesDirectory => inputImagesDirectory.Value; + + /// + /// Gets the full qualified path to the input test file. /// /// /// The file path. @@ -80,9 +86,9 @@ namespace ImageSharp.Tests /// /// The . /// - public static string GetPath(string file) + public static string GetInputFileFullPath(string file) { - return Path.Combine(FormatsDirectory, file); + return Path.Combine(InputImagesDirectory, file).Replace('\\', Path.DirectorySeparatorChar); } /// @@ -94,7 +100,7 @@ namespace ImageSharp.Tests /// public static TestFile Create(string file) { - return Cache.GetOrAdd(file, (string fileName) => new TestFile(GetPath(file))); + return Cache.GetOrAdd(file, (string fileName) => new TestFile(GetInputFileFullPath(file))); } /// @@ -106,7 +112,7 @@ namespace ImageSharp.Tests /// public string GetFileName(object value) { - return $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.file)}"; + return $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.FullPath)}"; } /// @@ -125,74 +131,22 @@ namespace ImageSharp.Tests /// Creates a new image. /// /// - /// The . + /// The . /// public Image CreateImage() { - return new Image(this.image); + return this.Image.Clone(); } /// /// Creates a new image. /// /// - /// The . + /// The . /// public Image CreateImage(IImageDecoder decoder) { - return Image.Load(this.image.Configuration, this.Bytes, decoder); - } - - /// - /// Gets the correct path to the formats directory. - /// - /// - /// The . - /// - private static string GetFormatsDirectory() - { - List directories = new List< string > { - "TestImages/Formats/", // Here for code coverage tests. - "tests/ImageSharp.Tests/TestImages/Formats/", // from travis/build script - "../../../../../ImageSharp.Tests/TestImages/Formats/", // from Sandbox46 - "../../../../TestImages/Formats/", - "../../../TestImages/Formats/" - }; - - directories = directories.SelectMany(x => new[] - { - Path.GetFullPath(x) - }).ToList(); - - AddFormatsDirectoryFromTestAssebmlyPath(directories); - - string directory = directories.FirstOrDefault(x => Directory.Exists(x)); - - if(directory != null) - { - return directory; - } - - throw new System.Exception($"Unable to find Formats directory at any of these locations [{string.Join(", ", directories)}]"); - } - - /// - /// The path returned by Path.GetFullPath(x) can be relative to dotnet framework directory - /// in certain scenarios like dotTrace test profiling. - /// This method calculates and adds the format directory based on the ImageSharp.Tests assembly location. - /// - /// The directories list - private static void AddFormatsDirectoryFromTestAssebmlyPath(List directories) - { - string assemblyLocation = typeof(TestFile).GetTypeInfo().Assembly.Location; - assemblyLocation = Path.GetDirectoryName(assemblyLocation); - - if (assemblyLocation != null) - { - string dirFromAssemblyLocation = Path.Combine(assemblyLocation, "../../../TestImages/Formats/"); - dirFromAssemblyLocation = Path.GetFullPath(dirFromAssemblyLocation); - directories.Add(dirFromAssemblyLocation); - } + return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); } } } diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs index d43b989f10..e388b35d45 100644 --- a/tests/ImageSharp.Tests/TestFileSystem.cs +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using ImageSharp.Formats; - using Xunit; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.Formats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// A test image file. /// @@ -22,7 +20,7 @@ namespace ImageSharp.Tests public static TestFileSystem Global { get; } = new TestFileSystem(); - public static void RegisterGloablTestFormat() + public static void RegisterGlobalTestFormat() { Configuration.Default.FileSystem = Global; } diff --git a/tests/ImageSharp.Tests/TestFont.cs b/tests/ImageSharp.Tests/TestFont.cs index 7e5b6c370d..6f805e3676 100644 --- a/tests/ImageSharp.Tests/TestFont.cs +++ b/tests/ImageSharp.Tests/TestFont.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// A test image file. /// diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 5a3cd102e7..078b708dfd 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; - /// /// A test image file. /// @@ -23,15 +20,15 @@ namespace ImageSharp.Tests { public static TestFormat GlobalTestFormat { get; } = new TestFormat(); - public static void RegisterGloablTestFormat() + public static void RegisterGlobalTestFormat() { Configuration.Default.Configure(GlobalTestFormat); } public TestFormat() { - this.Encoder = new TestEncoder(this); ; - this.Decoder = new TestDecoder(this); ; + this.Encoder = new TestEncoder(this); + this.Decoder = new TestDecoder(this); } public List DecodeCalls { get; } = new List(); @@ -200,7 +197,7 @@ namespace ImageSharp.Tests config = config }); - // TODO record this happend so we an verify it. + // TODO record this happend so we can verify it. return this.testFormat.Sample(); } @@ -222,7 +219,7 @@ namespace ImageSharp.Tests public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - // TODO record this happend so we an verify it. + // TODO record this happend so we can verify it. } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index f35720f199..864c963327 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Linq; +// ReSharper disable InconsistentNaming // ReSharper disable MemberHidesStaticFromOuterClass -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Linq; - /// /// Class that contains all the relative test image paths in the TestImages/Formats directory. /// Use with , or . @@ -25,7 +24,18 @@ namespace ImageSharp.Tests public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; + public const string Palette8Bpp = "Png/palette-8bpp.png"; + public const string Bpp1 = "Png/bpp1.png"; + public const string Gray4Bpp = "Png/gray_4bpp.png"; public const string Rgb48Bpp = "Png/rgb-48bpp.png"; + public const string CalliphoraPartial = "Png/CalliphoraPartial.png"; + public const string CalliphoraPartialGrayscale = "Png/CalliphoraPartialGrayscale.png"; + public const string Bike = "Png/Bike.png"; + public const string BikeGrayscale = "Png/BikeGrayscale.png"; + public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png"; + public const string SnakeGame = "Png/SnakeGame.png"; + public const string Icon = "Png/icon.png"; + public const string Kaboom = "Png/kaboom.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; @@ -37,11 +47,21 @@ namespace ImageSharp.Tests // Filter changing per scanline public const string FilterVar = "Png/filterVar.png"; + public const string VimImage1 = "Png/vim16x16_1.png"; + public const string VimImage2 = "Png/vim16x16_2.png"; + + public const string VersioningImage1 = "Png/versioning-1_1.png"; + public const string VersioningImage2 = "Png/versioning-1_2.png"; + + public const string Banner7Adam7InterlaceMode = "Png/banner7-adam.png"; + public const string Banner8Index = "Png/banner8-index.png"; + public static class Bad { // Odd chunk lengths public const string ChunkLength1 = "Png/chunklength1.png"; public const string ChunkLength2 = "Png/chunklength2.png"; + public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; } public static readonly string[] All = @@ -49,7 +69,8 @@ namespace ImageSharp.Tests P1, Pd, Blur, Splash, Cross, Powerpoint, SplashInterlaced, Interlaced, Filter0, Filter1, Filter2, Filter3, Filter4, - FilterVar + FilterVar, VimImage1, VimImage2, VersioningImage1, + VersioningImage2 }; } @@ -73,7 +94,7 @@ namespace ImageSharp.Tests { public static class Bad { - public const string MissingEOF = "Jpg/baseline/badeof.jpg"; + public const string BadEOF = "Jpg/baseline/badeof.jpg"; public const string ExifUndefType = "Jpg/baseline/ExifUndefType.jpg"; } @@ -88,27 +109,48 @@ namespace ImageSharp.Tests public const string Snake = "Jpg/baseline/Snake.jpg"; public const string Lake = "Jpg/baseline/Lake.jpg"; public const string Jpeg400 = "Jpg/baseline/jpeg400jfif.jpg"; - public const string Jpeg420 = "Jpg/baseline/jpeg420exif.jpg"; + public const string Jpeg420Exif = "Jpg/baseline/jpeg420exif.jpg"; public const string Jpeg444 = "Jpg/baseline/jpeg444.jpg"; - public const string Testimgorig = "Jpg/baseline/testorig.jpg"; + public const string Jpeg420Small = "Jpg/baseline/jpeg420small.jpg"; + public const string Testorig420 = "Jpg/baseline/testorig.jpg"; public static readonly string[] All = { Cmyk, Ycck, Exif, Floorplan, Calliphora, Turtle, GammaDalaiLamaGray, - Hiyamugi, Jpeg400, Jpeg420, Jpeg444, + Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, }; } + public class Issues + { + public const string CriticalEOF214 = "Jpg/issues/Issue214-CriticalEOF.jpg"; + public const string MissingFF00ProgressiveGirl159 = "Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg"; + public const string BadCoeffsProgressive178 = "Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg"; + public const string BadZigZagProgressive385 = "Jpg/issues/Issue385-BadZigZag-Progressive.jpg"; + public const string MultiHuffmanBaseline394 = "Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg"; + } + public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); } public static class Bmp { + // Note: The inverted images have been generated by altering the BitmapInfoHeader using a hex editor. + // As such, the expected pixel output will be the reverse of the unaltered equivalent images. public const string Car = "Bmp/Car.bmp"; public const string F = "Bmp/F.bmp"; public const string NegHeight = "Bmp/neg_height.bmp"; - public static readonly string[] All = { Car, F, NegHeight }; + public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; + public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; + public const string RLE = "Bmp/RunLengthEncoded.bmp"; + public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp"; + public const string Bit8 = "Bmp/test8.bmp"; + public const string Bit8Inverted = "Bmp/test8-inverted.bmp"; + public const string Bit16 = "Bmp/test16.bmp"; + public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; + + public static readonly string[] All = { Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted, Bit16, Bit16Inverted }; } public static class Gif @@ -117,8 +159,16 @@ namespace ImageSharp.Tests public const string Giphy = "Gif/giphy.gif"; public const string Cheers = "Gif/cheers.gif"; public const string Trans = "Gif/trans.gif"; + public const string Kumin = "Gif/kumin.gif"; + + public class Issues + { + public const string BadAppExtLength = "Gif/issues/issue405_badappextlength252.gif"; + public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif"; + public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif"; + } - public static readonly string[] All = { Rings, Giphy, Cheers, Trans }; + public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin }; } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 333b645dea..70d4df273e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -1,9 +1,12 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +namespace SixLabors.ImageSharp.Tests +{ internal struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer { private readonly float Eps; @@ -17,7 +20,7 @@ namespace ImageSharp.Tests { float d = x - y; - return d > -this.Eps && d < this.Eps; + return d >= -this.Eps && d <= this.Eps; } public int GetHashCode(float obj) diff --git a/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs b/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs index d3b4da9b92..e342f7029f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Linq; +using System.Linq; +namespace SixLabors.ImageSharp.Tests +{ public static class ArrayHelper { /// diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs new file mode 100644 index 0000000000..b2967058c0 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs @@ -0,0 +1,17 @@ +namespace SixLabors.ImageSharp.Tests +{ + using System; + + /// + /// The output produced by this test class should be grouped into the specified subfolder. + /// + public class GroupOutputAttribute : Attribute + { + public GroupOutputAttribute(string subfolder) + { + this.Subfolder = subfolder; + } + + public string Subfolder { get; } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index 05e8c61360..ec3254921f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - - using Xunit.Sdk; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit.Sdk; +namespace SixLabors.ImageSharp.Tests +{ /// /// Base class for Theory Data attributes which pass an instance of to the test case. /// @@ -54,7 +51,7 @@ namespace ImageSharp.Tests if (!string.IsNullOrWhiteSpace(this.MemberName)) { Type type = this.MemberType ?? testMethod.DeclaringType; - Func accessor = this.GetPropertyAccessor(type) ?? this.GetFieldAccessor(type); + Func accessor = this.GetPropertyAccessor(type, this.MemberName) ?? this.GetFieldAccessor(type, this.MemberName); if (accessor != null) { @@ -156,12 +153,12 @@ namespace ImageSharp.Tests /// /// Gets the field accessor for the given type. /// - Func GetFieldAccessor(Type type) + protected Func GetFieldAccessor(Type type, string memberName) { FieldInfo fieldInfo = null; for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - fieldInfo = reflectionType.GetRuntimeField(this.MemberName); + fieldInfo = reflectionType.GetRuntimeField(memberName); if (fieldInfo != null) break; } @@ -175,12 +172,12 @@ namespace ImageSharp.Tests /// /// Gets the property accessor for the given type. /// - Func GetPropertyAccessor(Type type) + protected Func GetPropertyAccessor(Type type, string memberName) { PropertyInfo propInfo = null; for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - propInfo = reflectionType.GetRuntimeProperty(this.MemberName); + propInfo = reflectionType.GetRuntimeProperty(memberName); if (propInfo != null) { break; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs index 72be5abc29..796cba8554 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which produce a blank image of size width * height. /// One instance will be passed for each the pixel format defined by the pixelTypes parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs index ec8421853e..e896c18546 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which read an image from the given file /// One instance will be passed for each the pixel format defined by the pixelTypes parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs index 1b37c45a92..1702513898 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName /// instances will be passed for each the pixel format defined by the pixelTypes parameter @@ -38,18 +36,18 @@ namespace ImageSharp.Tests /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName /// instances will be passed for each the pixel format defined by the pixelTypes parameter /// - /// The name of the static test class field/property enumerating the files + /// The name of the static test class field/property enumerating the files /// The member name for enumerating method parameters /// The requested pixel types /// Additional theory parameter values public WithFileCollectionAttribute( - string enumeratorMemberName, + string fileEnumeratorMemberName, string memberName, PixelTypes pixelTypes, params object[] additionalParameters) : base(memberName, pixelTypes, additionalParameters) { - this.fileEnumeratorMemberName = enumeratorMemberName; + this.fileEnumeratorMemberName = fileEnumeratorMemberName; } /// @@ -60,8 +58,8 @@ namespace ImageSharp.Tests /// The protected override IEnumerable GetAllFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { - Func accessor = this.GetPropertyAccessor(testMethod.DeclaringType); - accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType); + Func accessor = this.GetPropertyAccessor(testMethod.DeclaringType, this.fileEnumeratorMemberName); + accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType, this.fileEnumeratorMemberName); var files = (IEnumerable)accessor(); return files.Select(f => new object[] { f }); @@ -69,52 +67,5 @@ namespace ImageSharp.Tests /// protected override string GetFactoryMethodName(MethodInfo testMethod) => "File"; - - /// - /// Gets the field accessor for the given type. - /// - private Func GetFieldAccessor(Type type) - { - FieldInfo fieldInfo = null; - for (Type reflectionType = type; - reflectionType != null; - reflectionType = reflectionType.GetTypeInfo().BaseType) - { - fieldInfo = reflectionType.GetRuntimeField(this.fileEnumeratorMemberName); - if (fieldInfo != null) - { - break; - } - } - - if (fieldInfo == null || !fieldInfo.IsStatic) - { - return null; - } - - return () => fieldInfo.GetValue(null); - } - - /// - /// Gets the property accessor for the given type. - /// - private Func GetPropertyAccessor(Type type) - { - PropertyInfo propInfo = null; - for (Type reflectionType = type; - reflectionType != null; - reflectionType = reflectionType.GetTypeInfo().BaseType) - { - propInfo = reflectionType.GetRuntimeProperty(this.fileEnumeratorMemberName); - if (propInfo != null) break; - } - - if (propInfo?.GetMethod == null || !propInfo.GetMethod.IsStatic) - { - return null; - } - - return () => propInfo.GetValue(null, null); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index 6c6198c38d..cdf5fcb089 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Linq; - using System.Reflection; +using System; +using System.Linq; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which return the image produced by the given test class member method /// instances will be passed for each the pixel format defined by the pixelTypes parameter @@ -39,9 +37,8 @@ namespace ImageSharp.Tests Type colorType = args.Single(); Type imgType = typeof(Image<>).MakeGenericType(colorType); - Type genericFactoryType = (typeof(GenericFactory<>)).MakeGenericType(colorType); - Type funcType = typeof(Func<,>).MakeGenericType(genericFactoryType, imgType); + Type funcType = typeof(Func<>).MakeGenericType(imgType); MethodInfo genericMethod = m.MakeGenericMethod(args); diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs index 9a8538e78b..991f7108fe 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs @@ -1,12 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Reflection; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Reflection; + using SixLabors.ImageSharp.PixelFormats; /// /// Triggers passing instances which produce an image of size width * height filled with the requested color. @@ -58,14 +58,88 @@ namespace ImageSharp.Tests byte a, PixelTypes pixelTypes, params object[] additionalParameters) - : base(width, height, pixelTypes, additionalParameters) + : this(null, width, height, r, g, b, a, pixelTypes, additionalParameters) + { + } + + /// + /// Triggers passing instances which produce an image of size width * height filled with the requested color. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The member data to apply to theories + /// The width of the requested image + /// The height of the requested image + /// Red + /// Green + /// Blue + /// /// Alpha + /// The requested pixel types + /// Additional theory parameter values + public WithSolidFilledImagesAttribute( + string memberData, + int width, + int height, + byte r, + byte g, + byte b, + byte a, + PixelTypes pixelTypes, + params object[] additionalParameters) + : base(memberData, width, height, pixelTypes, additionalParameters) { this.R = r; this.G = g; this.B = b; this.A = a; } - + + /// + /// Triggers passing instances which produce an image of size width * height filled with the requested color. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The width of the requested image + /// The height of the requested image + /// The referenced color name (name of property in + /// The requested pixel types + /// Additional theory parameter values + public WithSolidFilledImagesAttribute( + int width, + int height, + string colorName, + PixelTypes pixelTypes, + params object[] additionalParameters) + : this(null, width, height, colorName, pixelTypes, additionalParameters) + { + } + + /// + /// Triggers passing instances which produce an image of size width * height filled with the requested color. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The member data to apply to theories + /// The width of the requested image + /// The height of the requested image + /// The referenced color name (name of property in + /// The requested pixel types + /// Additional theory parameter values + public WithSolidFilledImagesAttribute( + string memberData, + int width, + int height, + string colorName, + PixelTypes pixelTypes, + params object[] additionalParameters) + : base(memberData, width, height, pixelTypes, additionalParameters) + { + Guard.NotNull(colorName, nameof(colorName)); + + var c = (Rgba32)typeof(Rgba32).GetTypeInfo().GetField(colorName).GetValue(null); + this.R = c.R; + this.G = c.G; + this.B = c.B; + this.A = c.A; + } + /// /// Red /// diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs index f6ab65f714..7c659c64fc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which produce a blank image of size width * height. /// One instance will be passed for each the pixel format defined by the pixelTypes parameter @@ -22,7 +20,7 @@ namespace ImageSharp.Tests /// The requested parameter /// Additional theory parameter values public WithTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) - : this(null, width, height, pixelTypes,additionalParameters) + : this(null, width, height, pixelTypes, additionalParameters) { } diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs deleted file mode 100644 index 4a0950788d..0000000000 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System; - - using ImageSharp.PixelFormats; - - /// - /// Utility class to create specialized subclasses of generic classes (eg. ) - /// Used as parameter for -based factory methods - /// - public class GenericFactory - where TPixel : struct, IPixel - { - public virtual Image CreateImage(int width, int height) - { - return new Image(width, height); - } - - public virtual Image CreateImage(byte[] bytes) - { - return Image.Load(bytes); - } - - public virtual Image CreateImage(Image other) - { - return new Image(other); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs deleted file mode 100644 index 20af430a5f..0000000000 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using ImageSharp.PixelFormats; - - public class ImageFactory : GenericFactory - { - public override Image CreateImage(byte[] bytes) => Image.Load(bytes); - - public override Image CreateImage(int width, int height) => new Image(width, height); - - public override Image CreateImage(Image other) - { - return new Image(other); - } - } -} diff --git a/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs b/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs index dd3ef3a4f9..f7732fce53 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs @@ -1,9 +1,12 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +namespace SixLabors.ImageSharp.Tests +{ /// /// Allows the comparison of single-precision floating point values by precision. /// diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs new file mode 100644 index 0000000000..dbe2523661 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -0,0 +1,57 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.PixelFormats; + + using SixLabors.Primitives; + + public class ExactImageComparer : ImageComparer + { + public static ExactImageComparer Instance { get; } = new ExactImageComparer(); + + public override ImageSimilarityReport CompareImagesOrFrames( + ImageFrame expected, + ImageFrame actual) + { + if (expected.Size() != actual.Size()) + { + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); + } + + int width = actual.Width; + + // TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types. + + Rgba32[] aBuffer = new Rgba32[width]; + Rgba32[] bBuffer = new Rgba32[width]; + + var differences = new List(); + + for (int y = 0; y < actual.Height; y++) + { + Span aSpan = expected.GetPixelRowSpan(y); + Span bSpan = actual.GetPixelRowSpan(y); + + PixelOperations.Instance.ToRgba32(aSpan, aBuffer, width); + PixelOperations.Instance.ToRgba32(bSpan, bBuffer, width); + + for (int x = 0; x < width; x++) + { + Rgba32 aPixel = aBuffer[x]; + Rgba32 bPixel = bBuffer[x]; + + if (aPixel != bPixel) + { + var diff = new PixelDifference(new Point(x, y), aPixel, bPixel); + differences.Add(diff); + } + } + } + + return new ImageSimilarityReport(expected, actual, differences); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs new file mode 100644 index 0000000000..e5f031b504 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -0,0 +1,34 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + public class ImageDifferenceIsOverThresholdException : ImagesSimilarityException + { + public ImageSimilarityReport[] Reports { get; } + + public ImageDifferenceIsOverThresholdException(IEnumerable reports) + : base("Image difference is over threshold!" + StringifyReports(reports)) + { + this.Reports = reports.ToArray(); + } + + private static string StringifyReports(IEnumerable reports) + { + StringBuilder sb = new StringBuilder(); + sb.Append(Environment.NewLine); + + int i = 0; + foreach (ImageSimilarityReport r in reports) + { + sb.Append($"Report{i}: "); + sb.Append(r); + sb.Append(Environment.NewLine); + i++; + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs new file mode 100644 index 0000000000..e8f60ade31 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + public class ImageDimensionsMismatchException : ImagesSimilarityException + { + public ImageDimensionsMismatchException(Size expectedSize, Size actualSize) + : base((string)$"The image dimensions {actualSize} do not match the expected {expectedSize}!") + { + this.ExpectedSize = expectedSize; + this.ActualSize = actualSize; + } + + public Size ExpectedSize { get; } + public Size ActualSize { get; } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs new file mode 100644 index 0000000000..bbdb6b5815 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs @@ -0,0 +1,12 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + + public class ImagesSimilarityException : Exception + { + public ImagesSimilarityException(string message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs new file mode 100644 index 0000000000..920e633795 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -0,0 +1,135 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + public abstract class ImageComparer + { + public static ImageComparer Exact { get; } = Tolerant(0, 0); + + /// + /// Returns an instance of . + /// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'. + /// + public static ImageComparer Tolerant( + float imageThreshold = TolerantImageComparer.DefaultImageThreshold, + int perPixelManhattanThreshold = 0) + { + return new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); + } + + public abstract ImageSimilarityReport CompareImagesOrFrames( + ImageFrame expected, + ImageFrame actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel; + } + + public static class ImageComparerExtensions + { + public static ImageSimilarityReport CompareImagesOrFrames( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + return comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame); + } + + public static IEnumerable> CompareImages( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + var result = new List>(); + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new Exception("Frame count does not match!"); + } + for (int i = 0; i < expected.Frames.Count; i++) + { + ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]); + if (!report.IsEmpty) + { + result.Add(report); + } + } + + return result; + } + + public static void VerifySimilarity( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + if (expected.Size() != actual.Size()) + { + throw new ImageDimensionsMismatchException(expected.Size(), actual.Size()); + } + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new ImagesSimilarityException("Image frame count does not match!"); + } + + IEnumerable reports = comparer.CompareImages(expected, actual); + if (reports.Any()) + { + throw new ImageDifferenceIsOverThresholdException(reports); + } + } + + public static void VerifySimilarityIgnoreRegion( + this ImageComparer comparer, + Image expected, + Image actual, + Rectangle ignoredRegion) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel + { + if (expected.Size() != actual.Size()) + { + throw new ImageDimensionsMismatchException(expected.Size(), actual.Size()); + } + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new ImagesSimilarityException("Image frame count does not match!"); + } + + IEnumerable> reports = comparer.CompareImages(expected, actual); + if (reports.Any()) + { + List> cleanedReports = new List>(reports.Count()); + foreach (var r in reports) + { + var outsideChanges = r.Differences.Where(x => !( + ignoredRegion.X <= x.Position.X && + x.Position.X <= ignoredRegion.Right && + ignoredRegion.Y <= x.Position.Y && + x.Position.Y <= ignoredRegion.Bottom)); + if (outsideChanges.Any()) + { + cleanedReports.Add(new ImageSimilarityReport(r.ExpectedImage, r.ActualImage, outsideChanges, null)); + } + } + + if (cleanedReports.Any()) + { + throw new ImageDifferenceIsOverThresholdException(cleanedReports); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs new file mode 100644 index 0000000000..9501a6c88b --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -0,0 +1,102 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using SixLabors.ImageSharp.PixelFormats; + + public class ImageSimilarityReport + { + protected ImageSimilarityReport( + object expectedImage, + object actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) + { + this.ExpectedImage = expectedImage; + this.ActualImage = actualImage; + this.TotalNormalizedDifference = totalNormalizedDifference; + this.Differences = differences.ToArray(); + } + public object ExpectedImage { get; } + + public object ActualImage { get; } + + // TODO: This should not be a nullable value! + public float? TotalNormalizedDifference { get; } + + public string DifferencePercentageString + { + get + { + if (!this.TotalNormalizedDifference.HasValue) + { + return "?"; + } + else if (this.TotalNormalizedDifference == 0) + { + return "0%"; + } + else + { + return $"{this.TotalNormalizedDifference.Value * 100:0.0000}%"; + } + } + } + + public PixelDifference[] Differences { get; } + + public bool IsEmpty => this.Differences.Length == 0; + + public override string ToString() + { + return this.IsEmpty ? "[SimilarImages]" : this.PrintDifference(); + } + + private string PrintDifference() + { + var sb = new StringBuilder(); + if (this.TotalNormalizedDifference.HasValue) + { + sb.AppendLine($"Total difference: {this.DifferencePercentageString}"); + } + int max = Math.Min(5, this.Differences.Length); + + for (int i = 0; i < max; i++) + { + sb.Append(this.Differences[i]); + if (i < max - 1) + { + sb.Append("; "); + } + } + if (this.Differences.Length >= 5) + { + sb.Append("..."); + } + return sb.ToString(); + } + } + + public class ImageSimilarityReport : ImageSimilarityReport + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel + { + public ImageSimilarityReport( + ImageFrame expectedImage, + ImageFrame actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) + : base(expectedImage, actualImage, differences, totalNormalizedDifference) + { + } + + public static ImageSimilarityReport Empty => + new ImageSimilarityReport(null, null, Enumerable.Empty(), 0f); + + public new ImageFrame ExpectedImage => (ImageFrame)base.ExpectedImage; + + public new ImageFrame ActualImage => (ImageFrame)base.ActualImage; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs new file mode 100644 index 0000000000..fcfb31d682 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs @@ -0,0 +1,40 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using SixLabors.Primitives; + + public struct PixelDifference + { + public PixelDifference( + Point position, + int redDifference, + int greenDifference, + int blueDifference, + int alphaDifference) + { + this.Position = position; + this.RedDifference = redDifference; + this.GreenDifference = greenDifference; + this.BlueDifference = blueDifference; + this.AlphaDifference = alphaDifference; + } + + public PixelDifference(Point position, Rgba32 expected, Rgba32 actual) + : this(position, + (int)actual.R - (int)expected.R, + (int)actual.G - (int)expected.G, + (int)actual.B - (int)expected.B, + (int)actual.A - (int)expected.A) + { + } + + public Point Position { get; } + + public int RedDifference { get; } + public int GreenDifference { get; } + public int BlueDifference { get; } + public int AlphaDifference { get; } + + public override string ToString() => + $"[Δ({this.RedDifference},{this.GreenDifference},{this.BlueDifference},{this.AlphaDifference}) @ ({this.Position.X},{this.Position.Y})]"; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs new file mode 100644 index 0000000000..e68a1fbfea --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -0,0 +1,117 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.PixelFormats; + + using SixLabors.Primitives; + + public class TolerantImageComparer : ImageComparer + { + // 1% of all pixels in a 100*100 pixel area are allowed to have a difference of 1 unit + public const float DefaultImageThreshold = 1.0f / (100 * 100 * 255); + + /// + /// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'. + /// + public TolerantImageComparer(float imageThreshold, int perPixelManhattanThreshold = 0) + { + Guard.MustBeGreaterThanOrEqualTo(imageThreshold, 0, nameof(imageThreshold)); + + this.ImageThreshold = imageThreshold; + this.PerPixelManhattanThreshold = perPixelManhattanThreshold; + } + + /// + /// The maximal tolerated difference represented by a value between 0.0 and 1.0. + /// Examples of percentage differences on a single pixel: + /// 1. PixelA = (255,255,255,0) PixelB =(0,0,0,255) leads to 100% difference on a single pixel + /// 2. PixelA = (255,255,255,0) PixelB =(255,255,255,255) leads to 25% difference on a single pixel + /// 3. PixelA = (255,255,255,0) PixelB =(128,128,128,128) leads to 50% difference on a single pixel + /// + /// The total differences is the sum of all pixel differences normalized by image dimensions! + /// The individual distances are calculated using the Manhattan function: + /// + /// https://en.wikipedia.org/wiki/Taxicab_geometry + /// + /// ImageThresholdInPercents = 1.0/255 means that we allow one byte difference per channel on a 1x1 image + /// ImageThresholdInPercents = 1.0/(100*100*255) means that we allow only one byte difference per channel on a 100x100 image + /// + public float ImageThreshold { get; } + + /// + /// The threshold of the individual pixels before they acumulate towards the overall difference. + /// For an individual pixel pair the value is the Manhattan distance of pixels: + /// + /// https://en.wikipedia.org/wiki/Taxicab_geometry + /// + /// + public int PerPixelManhattanThreshold { get; } + + public override ImageSimilarityReport CompareImagesOrFrames(ImageFrame expected, ImageFrame actual) + { + if (expected.Size() != actual.Size()) + { + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); + } + + int width = actual.Width; + + // TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types. + + Rgba32[] aBuffer = new Rgba32[width]; + Rgba32[] bBuffer = new Rgba32[width]; + + float totalDifference = 0.0f; + + var differences = new List(); + + for (int y = 0; y < actual.Height; y++) + { + Span aSpan = expected.GetPixelRowSpan(y); + Span bSpan = actual.GetPixelRowSpan(y); + + PixelOperations.Instance.ToRgba32(aSpan, aBuffer, width); + PixelOperations.Instance.ToRgba32(bSpan, bBuffer, width); + + for (int x = 0; x < width; x++) + { + int d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]); + + if (d > this.PerPixelManhattanThreshold) + { + var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]); + differences.Add(diff); + + totalDifference += d; + } + } + } + + float normalizedDifference = totalDifference / ((float)actual.Width * (float)actual.Height); + normalizedDifference /= 4.0f * 255.0f; + + if (normalizedDifference > this.ImageThreshold) + { + return new ImageSimilarityReport(expected, actual, differences, normalizedDifference); + } + else + { + return ImageSimilarityReport.Empty; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetManhattanDistanceInRgbaSpace(ref Rgba32 a, ref Rgba32 b) + { + return Diff(a.R, b.R) + Diff(a.G, b.G) + Diff(a.B, b.B) + Diff(a.A, b.A); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Diff(byte a, byte b) => Math.Abs(a - b); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 4252a60b5e..78821aac15 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public abstract partial class TestImageProvider where TPixel : struct, IPixel { @@ -33,7 +31,7 @@ namespace ImageSharp.Tests protected int Width { get; private set; } - public override Image GetImage() => this.Factory.CreateImage(this.Width, this.Height); + public override Image GetImage() => new Image(this.Width, this.Height); public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 16eecd2fc7..92332eba08 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public abstract partial class TestImageProvider where TPixel : struct, IPixel { @@ -19,47 +20,135 @@ namespace ImageSharp.Tests { // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider // are shared between PixelTypes.Color & PixelTypes.Rgba32 - private class Key : Tuple + private class Key : IEquatable { - public Key(PixelTypes item1, string item2) - : base(item1, item2) + private Tuple commonValues; + + private Dictionary decoderParameters; + + public Key(PixelTypes pixelType, string filePath, IImageDecoder customDecoder) { + Type customType = customDecoder?.GetType(); + this.commonValues = new Tuple(pixelType, filePath, customType); + this.decoderParameters = GetDecoderParameters(customDecoder); + } + + private static Dictionary GetDecoderParameters(IImageDecoder customDecoder) + { + Type type = customDecoder.GetType(); + + var data = new Dictionary(); + + while (type != null && type != typeof(object)) + { + PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + { + string key = $"{type.FullName}.{p.Name}"; + object value = p.GetValue(customDecoder); + data[key] = value; + } + type = type.GetTypeInfo().BaseType; + } + return data; + } + + public bool Equals(Key other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!this.commonValues.Equals(other.commonValues)) return false; + + if (this.decoderParameters.Count != other.decoderParameters.Count) + { + return false; + } + + foreach (KeyValuePair kv in this.decoderParameters) + { + object otherVal; + if (!other.decoderParameters.TryGetValue(kv.Key, out otherVal)) + { + return false; + } + if (!object.Equals(kv.Value, otherVal)) + { + return false; + } + } + return true; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return this.Equals((Key)obj); + } + + public override int GetHashCode() + { + return this.commonValues.GetHashCode(); + } + + public static bool operator ==(Key left, Key right) + { + return Equals(left, right); + } + + public static bool operator !=(Key left, Key right) + { + return !Equals(left, right); } } private static readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); - private string filePath; - - public FileProvider(string filePath) + // Needed for deserialization! + // ReSharper disable once UnusedMember.Local + public FileProvider() { - this.filePath = filePath; } - public FileProvider() + public FileProvider(string filePath) { + this.FilePath = filePath; } + + /// + /// Gets the file path relative to the "~/tests/images" folder + /// + public string FilePath { get; private set; } - public override string SourceFileOrDescription => this.filePath; + public override string SourceFileOrDescription => this.FilePath; public override Image GetImage() { - Key key = new Key(this.PixelType, this.filePath); + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath); + return this.GetImage(decoder); + } + + public override Image GetImage(IImageDecoder decoder) + { + Guard.NotNull(decoder, nameof(decoder)); + + Key key = new Key(this.PixelType, this.FilePath, decoder); Image cachedImage = cache.GetOrAdd( key, fn => { - TestFile testFile = TestFile.Create(this.filePath); - return this.Factory.CreateImage(testFile.Bytes); + TestFile testFile = TestFile.Create(this.FilePath); + return Image.Load(testFile.Bytes, decoder); }); - return this.Factory.CreateImage(cachedImage); + return cachedImage.Clone(); } public override void Deserialize(IXunitSerializationInfo info) { - this.filePath = info.GetValue("path"); + this.FilePath = info.GetValue("path"); base.Deserialize(info); // must be called last } @@ -67,8 +156,14 @@ namespace ImageSharp.Tests public override void Serialize(IXunitSerializationInfo info) { base.Serialize(info); - info.AddValue("path", this.filePath); + info.AddValue("path", this.FilePath); } } + + public static string GetFilePathOrNull(ITestImageProvider provider) + { + var fileProvider = provider as FileProvider; + return fileProvider?.FilePath; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs index 30e7a63b50..5bd53a4c0c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ /// /// Provides instances for parametric unit tests. /// @@ -18,14 +16,14 @@ namespace ImageSharp.Tests { private class LambdaProvider : TestImageProvider { - private readonly Func, Image> creator; + private readonly Func> factoryFunc; - public LambdaProvider(Func, Image> creator) + public LambdaProvider(Func> factoryFunc) { - this.creator = creator; + this.factoryFunc = factoryFunc; } - public override Image GetImage() => this.creator(this.Factory); + public override Image GetImage() => this.factoryFunc(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 0caded4201..32860d2a4d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ /// /// Provides instances for parametric unit tests. /// @@ -18,7 +16,7 @@ namespace ImageSharp.Tests public abstract partial class TestImageProvider where TPixel : struct, IPixel { - private class SolidProvider : BlankProvider + private class SolidProvider : BlankProvider { private byte a; @@ -55,9 +53,10 @@ namespace ImageSharp.Tests TPixel color = default(TPixel); color.PackFromRgba32(new Rgba32(this.r, this.g, this.b, this.a)); - return image.Fill(color); + image.Mutate(x => x.Fill(color)); + return image; } - + public override void Serialize(IXunitSerializationInfo info) { info.AddValue("red", this.r); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 03a685d34f..1352a2476a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -1,17 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; - using ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests +{ + using Castle.Core.Internal; - using Xunit.Abstractions; - public interface ITestImageProvider + public interface ITestImageProvider { PixelTypes PixelType { get; } ImagingTestCaseUtility Utility { get; } @@ -32,9 +34,9 @@ namespace ImageSharp.Tests /// public ImagingTestCaseUtility Utility { get; private set; } - public GenericFactory Factory { get; private set; } = new GenericFactory(); public string TypeName { get; private set; } public string MethodName { get; private set; } + public string OutputSubfolderName { get; private set; } public static TestImageProvider TestPattern( int width, @@ -59,10 +61,10 @@ namespace ImageSharp.Tests } public static TestImageProvider Lambda( - Func, Image> func, + Func> factoryFunc, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - => new LambdaProvider(func).Init(testMethod, pixelTypeOverride); + => new LambdaProvider(factoryFunc).Init(testMethod, pixelTypeOverride); public static TestImageProvider Solid( int width, @@ -82,13 +84,29 @@ namespace ImageSharp.Tests /// public abstract Image GetImage(); + public virtual Image GetImage(IImageDecoder decoder) + { + throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); + } + + /// + /// Returns an instance to the test case with the necessary traits. + /// + public Image GetImage(Action> operationsToApply) + { + var img = GetImage(); + img.Mutate(operationsToApply); + return img; + } + public virtual void Deserialize(IXunitSerializationInfo info) { PixelTypes pixelType = info.GetValue("PixelType"); string typeName = info.GetValue("TypeName"); string methodName = info.GetValue("MethodName"); + string outputSubfolderName = info.GetValue("OutputSubfolderName"); - this.Init(typeName, methodName, pixelType); + this.Init(typeName, methodName, outputSubfolderName, pixelType); } public virtual void Serialize(IXunitSerializationInfo info) @@ -96,9 +114,14 @@ namespace ImageSharp.Tests info.AddValue("PixelType", this.PixelType); info.AddValue("TypeName", this.TypeName); info.AddValue("MethodName", this.MethodName); + info.AddValue("OutputSubfolderName", this.OutputSubfolderName); } - protected TestImageProvider Init(string typeName, string methodName, PixelTypes pixelTypeOverride) + protected TestImageProvider Init( + string typeName, + string methodName, + string outputSubfolerName, + PixelTypes pixelTypeOverride) { if (pixelTypeOverride != PixelTypes.Undefined) { @@ -106,11 +129,7 @@ namespace ImageSharp.Tests } this.TypeName = typeName; this.MethodName = methodName; - - if (pixelTypeOverride == PixelTypes.Rgba32) - { - this.Factory = new ImageFactory() as GenericFactory; - } + this.OutputSubfolderName = outputSubfolerName; this.Utility = new ImagingTestCaseUtility { @@ -120,7 +139,7 @@ namespace ImageSharp.Tests if (methodName != null) { - this.Utility.Init(typeName, methodName); + this.Utility.Init(typeName, methodName, outputSubfolerName); } return this; @@ -128,7 +147,9 @@ namespace ImageSharp.Tests protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) { - return Init(testMethod?.DeclaringType.Name, testMethod?.Name, pixelTypeOverride); + string subfolder = testMethod?.DeclaringType.GetAttribute()?.Subfolder + ?? string.Empty; + return this.Init(testMethod?.DeclaringType.Name, testMethod?.Name, subfolder, pixelTypeOverride); } public override string ToString() diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 96d38fc401..0b48170879 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Numerics; +using System; +using System.Collections.Generic; +using System.Numerics; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public abstract partial class TestImageProvider where TPixel : struct, IPixel { @@ -49,7 +47,7 @@ namespace ImageSharp.Tests } } - return new Image(testImages[this.SourceFileOrDescription]); + return testImages[this.SourceFileOrDescription].Clone(); } /// @@ -62,7 +60,7 @@ namespace ImageSharp.Tests using (PixelAccessor pixels = image.Lock()) { BlackWhiteChecker(pixels); // top left - VirticalBars(pixels); // top right + VerticalBars(pixels); // top right TransparentGradients(pixels); // bottom left Rainbow(pixels); // bottom right } @@ -71,7 +69,7 @@ namespace ImageSharp.Tests /// Fills the top right quadrant with alternating solid vertical bars. /// /// - private static void VirticalBars(PixelAccessor pixels) + private static void VerticalBars(PixelAccessor pixels) { // topLeft int left = pixels.Width / 2; @@ -79,10 +77,15 @@ namespace ImageSharp.Tests int top = 0; int bottom = pixels.Height / 2; int stride = pixels.Width / 12; + if (stride < 1) + { + stride = 1; + } + TPixel[] c = { - NamedColors.HotPink, - NamedColors.Blue - }; + NamedColors.HotPink, + NamedColors.Blue + }; int p = 0; for (int y = top; y < bottom; y++) { @@ -111,9 +114,9 @@ namespace ImageSharp.Tests int bottom = pixels.Height / 2; int stride = pixels.Width / 6; TPixel[] c = { - NamedColors.Black, - NamedColors.White - }; + NamedColors.Black, + NamedColors.White + }; int p = 0; for (int y = top; y < bottom; y++) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 47085cf5f4..e7dfe54881 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -1,23 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ /// /// Utility class to provide information about the test image & the test case for the test code, /// and help managing IO. /// - public class ImagingTestCaseUtility : TestBase + public class ImagingTestCaseUtility { /// /// Name of the TPixel in the owner @@ -35,17 +34,14 @@ namespace ImageSharp.Tests /// public string TestGroupName { get; set; } = string.Empty; + public string OutputSubfolderName { get; set; } = string.Empty; + /// /// The name of the test case (by default) /// public string TestName { get; set; } = string.Empty; - /// - /// Gets the recommended file name for the output of the test - /// - /// - /// The required extension - public string GetTestOutputFileName(string extension = null, string tag = null) + private string GetTestOutputFileNameImpl(string extension, string details, bool appendPixelTypeToFileName) { string fn = string.Empty; @@ -65,6 +61,7 @@ namespace ImageSharp.Tests { extension = ".bmp"; } + extension = extension.ToLower(); if (extension[0] != '.') { @@ -73,22 +70,68 @@ namespace ImageSharp.Tests if (fn != string.Empty) fn = '_' + fn; - string pixName = this.PixelTypeName; - if (pixName != string.Empty) + string pixName = ""; + + if (appendPixelTypeToFileName) { - pixName = '_' + pixName; + pixName = this.PixelTypeName; + + if (pixName != string.Empty) + { + pixName = '_' + pixName; + } } - tag = tag ?? string.Empty; - if (tag != string.Empty) + details = details ?? string.Empty; + if (details != string.Empty) { - tag = '_' + tag; + details = '_' + details; } + return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{details}{extension}"; + } + + private static string Inv(FormattableString formattable) => System.FormattableString.Invariant(formattable); + + /// + /// Gets the recommended file name for the output of the test + /// + /// The required extension + /// The settings modifying the output path + /// A boolean indicating whether to append the pixel type to output file name. + /// The file test name + public string GetTestOutputFileName(string extension = null, object testOutputDetails = null, bool appendPixelTypeToFileName = true) + { + string detailsString = null; + string s = testOutputDetails as string; - return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{tag}{extension}"; + if (s != null) + { + detailsString = s; + } + else if (testOutputDetails != null) + { + Type type = testOutputDetails.GetType(); + TypeInfo info = type.GetTypeInfo(); + if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) + { + detailsString = Inv($"{testOutputDetails}"); + } + else + { + IEnumerable properties = testOutputDetails.GetType().GetRuntimeProperties(); + + detailsString = string.Join( + "_", + properties.ToDictionary(x => x.Name, x => x.GetValue(testOutputDetails)) + .Select(x => Inv($"{x.Key}-{x.Value}")) + ); + } + } + return this.GetTestOutputFileNameImpl(extension, detailsString, appendPixelTypeToFileName); } + /// /// Encodes image by the format matching the required extension, than saves it to the recommended output file. /// @@ -96,42 +139,104 @@ namespace ImageSharp.Tests /// The image instance /// The requested extension /// Optional encoder - /// Optional encoder options - public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, string tag = null) + public string SaveTestOutputFile( + Image image, + string extension = null, + IImageEncoder encoder = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) where TPixel : struct, IPixel { - string path = this.GetTestOutputFileName(extension: extension, tag:tag); - extension = Path.GetExtension(path); - encoder = encoder ?? GetImageFormatByExtension(extension); + string path = this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + encoder = encoder ?? TestEnvironment.GetReferenceEncoder(path); using (FileStream stream = File.OpenWrite(path)) { image.Save(stream, encoder); } + return path; } - internal void Init(string typeName, string methodName) + internal string GetReferenceOutputFileName( + string extension, + object settings, + bool appendPixelTypeToFileName) + { + return TestEnvironment.GetReferenceOutputFileName( + this.GetTestOutputFileName(extension, settings, appendPixelTypeToFileName) + ); + } + + internal void Init(string typeName, string methodName, string outputSubfolderName) { this.TestGroupName = typeName; this.TestName = methodName; + this.OutputSubfolderName = outputSubfolderName; } - - internal void Init(MethodInfo method) + + internal string GetTestOutputDir() { - this.Init(method.DeclaringType.Name, method.Name); + string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); + + if (!string.IsNullOrEmpty(this.OutputSubfolderName)) + { + testGroupName = Path.Combine(this.OutputSubfolderName, testGroupName); + } + + return TestEnvironment.CreateOutputDirectory(testGroupName); } - private static IImageEncoder GetImageFormatByExtension(string extension) + public static void ModifyPixel(Image img, int x, int y, byte perChannelChange) + where TPixel : struct, IPixel { - extension = extension?.TrimStart('.'); - var format = Configuration.Default.FindFormatByFileExtensions(extension); - return Configuration.Default.FindEncoder(format); + ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange); } - private string GetTestOutputDir() + public static void ModifyPixel(ImageFrame img, int x, int y, byte perChannelChange) + where TPixel : struct, IPixel { - string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); - return CreateOutputDirectory(testGroupName); + TPixel pixel = img[x, y]; + var rgbaPixel = default(Rgba32); + pixel.ToRgba32(ref rgbaPixel); + + if (rgbaPixel.R + perChannelChange <= 255) + { + rgbaPixel.R += perChannelChange; + } + else + { + rgbaPixel.R -= perChannelChange; + } + + if (rgbaPixel.G + perChannelChange <= 255) + { + rgbaPixel.G += perChannelChange; + } + else + { + rgbaPixel.G -= perChannelChange; + } + + if (rgbaPixel.B + perChannelChange <= 255) + { + rgbaPixel.B += perChannelChange; + } + else + { + rgbaPixel.B -= perChannelChange; + } + + if (rgbaPixel.A + perChannelChange <= 255) + { + rgbaPixel.A += perChannelChange; + } + else + { + rgbaPixel.A -= perChannelChange; + } + + pixel.PackFromRgba32(rgbaPixel); + img[x, y] = pixel; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs index 1a6bf5d8b8..c892c09de6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs +++ b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - - using Xunit.Abstractions; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ /// /// Utility class to measure the execution of an operation. It can be used either by inheritance or by composition. /// - public class MeasureFixture : TestBase + public class MeasureFixture { /// /// Value indicating whether priniting is enabled. @@ -59,4 +56,27 @@ namespace ImageSharp.Tests protected ITestOutputHelper Output { get; } } + + public class MeasureGuard : IDisposable + { + private readonly string operation; + + private readonly Stopwatch stopwatch = new Stopwatch(); + + public MeasureGuard(ITestOutputHelper output, string operation) + { + this.operation = operation; + this.Output = output; + this.Output.WriteLine(operation + " ..."); + this.stopwatch.Start(); + } + + private ITestOutputHelper Output { get; } + + public void Dispose() + { + this.stopwatch.Stop(); + this.Output.WriteLine($"{this.operation} completed in {this.stopwatch.ElapsedMilliseconds}ms"); + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index 645a4dc596..a8f7acb406 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; +namespace SixLabors.ImageSharp.Tests +{ /// /// Flags that are mapped to PackedPixel types. /// They trigger the desired parametrization for . /// [Flags] - public enum PixelTypes : uint + public enum PixelTypes { Undefined = 0, @@ -52,11 +50,11 @@ namespace ImageSharp.Tests Short4 = 1 << 17, - Rgb24 = 18, + Rgb24 = 1 << 18, - Bgr24 = 19, + Bgr24 = 1 << 19, - Bgra32 = 20, + Bgra32 = 1 << 20, // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs new file mode 100644 index 0000000000..a201b39bd2 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -0,0 +1,192 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Drawing.Imaging; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public static class SystemDrawingBridge + { + // TODO: It would be nice to have this method in PixelOperations + private static void ToArgb32(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgba32 s = ref rgbaBuffer[i]; + ref Argb32 d = ref dest[i]; + + d.PackFromRgba32(s); + } + } + } + + private static void FromArgb32(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgba32 s = ref rgbaBuffer[i]; + ref TPixel d = ref dest[i]; + + d.PackFromRgba32(s); + } + } + } + + private static void FromRgb24(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgb24(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgb24 s = ref rgbaBuffer[i]; + ref TPixel d = ref dest[i]; + var rgba = default(Rgba32); + s.ToRgba32(ref rgba); + + d.PackFromRgba32(rgba); + } + } + } + + internal static unsafe Image FromFromArgb32SystemDrawingBitmap(System.Drawing.Bitmap bmp) + where TPixel : struct, IPixel + { + int w = bmp.Width; + int h = bmp.Height; + + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + + if (bmp.PixelFormat != PixelFormat.Format32bppArgb) + { + throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Argb32!", nameof(bmp)); + } + + BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); + byte* sourcePtrBase = (byte*)data.Scan0; + + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Argb32); + + var image = new Image(w, h); + + using (var workBuffer = new Buffer(w)) + { + var destPtr = (Argb32*)workBuffer.Pin(); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + + byte* sourcePtr = sourcePtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + + FromArgb32(workBuffer, row); + } + } + + return image; + } + + /// + /// TODO: Doesn not work yet! + /// + internal static unsafe Image FromFromRgb24SystemDrawingBitmap(System.Drawing.Bitmap bmp) + where TPixel : struct, IPixel + { + int w = bmp.Width; + int h = bmp.Height; + + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + + if (bmp.PixelFormat != PixelFormat.Format24bppRgb) + { + throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Rgb24!", nameof(bmp)); + } + + BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); + byte* sourcePtrBase = (byte*)data.Scan0; + + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Rgb24); + + var image = new Image(w, h); + + using (var workBuffer = new Buffer(w)) + { + var destPtr = (Rgb24*)workBuffer.Pin(); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + + byte* sourcePtr = sourcePtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + + FromRgb24(workBuffer, row); + } + } + + return image; + } + + internal static unsafe System.Drawing.Bitmap ToSystemDrawingBitmap(Image image) + where TPixel : struct, IPixel + { + int w = image.Width; + int h = image.Height; + + var resultBitmap = new System.Drawing.Bitmap(w, h, PixelFormat.Format32bppArgb); + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + BitmapData data = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat); + byte* destPtrBase = (byte*)data.Scan0; + + long destRowByteCount = data.Stride; + long sourceRowByteCount = w * sizeof(Argb32); + + using (var workBuffer = new Buffer(w)) + { + var sourcePtr = (Argb32*)workBuffer.Pin(); + + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + ToArgb32(row, workBuffer); + byte* destPtr = destPtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + } + } + + resultBitmap.UnlockBits(data); + + return resultBitmap; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs new file mode 100644 index 0000000000..0e967e9278 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector + { + public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); + + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + { + if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb) + { + return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sourceBitmap); + } + + using (var convertedBitmap = new System.Drawing.Bitmap( + sourceBitmap.Width, + sourceBitmap.Height, + System.Drawing.Imaging.PixelFormat.Format32bppArgb)) + { + using (var g = System.Drawing.Graphics.FromImage(convertedBitmap)) + { + g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; + + g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); + } + return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(convertedBitmap); + } + } + } + + public IImageInfo Identify(Configuration configuration, Stream stream) + { + using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + { + var pixelType = new PixelTypeInfo(System.Drawing.Image.GetPixelFormatSize(sourceBitmap.PixelFormat)); + return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetaData()); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs new file mode 100644 index 0000000000..e1ef68fa62 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.Text; + +using System.Drawing.Imaging; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public class SystemDrawingReferenceEncoder : IImageEncoder + { + private readonly System.Drawing.Imaging.ImageFormat imageFormat; + + public SystemDrawingReferenceEncoder(ImageFormat imageFormat) + { + this.imageFormat = imageFormat; + } + + public static SystemDrawingReferenceEncoder Png { get; } = new SystemDrawingReferenceEncoder(System.Drawing.Imaging.ImageFormat.Png); + + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel + { + using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.ToSystemDrawingBitmap(image)) + { + sdBitmap.Save(stream, this.imageFormat); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs new file mode 100644 index 0000000000..9eb051e7a7 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs @@ -0,0 +1,32 @@ +using System; + +namespace SixLabors.ImageSharp.Tests +{ + internal static class TestDataGenerator + { + public static float[] GenerateRandomFloatArray(this Random rnd, int length, float minVal, float maxVal) + { + float[] values = new float[length]; + + for (int i = 0; i < length; i++) + { + values[i] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal; + } + + return values; + } + + public static float[] GenerateRandomRoundedFloatArray(this Random rnd, int length, int minVal, int maxValExclusive) + { + float[] values = new float[length]; + + for (int i = 0; i < length; i++) + { + int val = rnd.Next(minVal, maxValExclusive); + values[i] = (float)val; + } + + return values; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs new file mode 100644 index 0000000000..089fed6b0a --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +namespace SixLabors.ImageSharp.Tests +{ + public static partial class TestEnvironment + { + private static Lazy configuration = new Lazy(CreateDefaultConfiguration); + + internal static Configuration Configuration => configuration.Value; + + internal static IImageDecoder GetReferenceDecoder(string filePath) + { + IImageFormat format = GetImageFormat(filePath); + return Configuration.FindDecoder(format); + } + + internal static IImageEncoder GetReferenceEncoder(string filePath) + { + IImageFormat format = GetImageFormat(filePath); + return Configuration.FindEncoder(format); + } + + internal static IImageFormat GetImageFormat(string filePath) + { + string extension = Path.GetExtension(filePath).ToLower(); + if (extension[0] == '.') extension = extension.Substring(1); + IImageFormat format = Configuration.FindFormatByFileExtension(extension); + return format; + } + + private static void ConfigureCodecs( + this Configuration cfg, + IImageFormat imageFormat, + IImageDecoder decoder, + IImageEncoder encoder, + IImageFormatDetector detector) + { + cfg.SetDecoder(imageFormat, decoder); + cfg.SetEncoder(imageFormat, encoder); + cfg.AddImageFormatDetector(detector); + } + + private static Configuration CreateDefaultConfiguration() + { + var configuration = new Configuration( + new PngConfigurationModule(), + new JpegConfigurationModule(), + new GifConfigurationModule() + ); + + if (!IsLinux) + { + configuration.ConfigureCodecs( + ImageFormats.Png, + SystemDrawingReferenceDecoder.Instance, + SystemDrawingReferenceEncoder.Png, + new PngImageFormatDetector()); + + configuration.ConfigureCodecs( + ImageFormats.Bmp, + SystemDrawingReferenceDecoder.Instance, + SystemDrawingReferenceEncoder.Png, + new PngImageFormatDetector()); + } + else + { + configuration.Configure(new PngConfigurationModule()); + configuration.Configure(new BmpConfigurationModule()); + } + + return configuration; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs new file mode 100644 index 0000000000..d2282f3994 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -0,0 +1,123 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Tests +{ + public static partial class TestEnvironment + { + private const string ImageSharpSolutionFileName = "ImageSharp.sln"; + + private const string InputImagesRelativePath = @"tests\Images\Input"; + + private const string ActualOutputDirectoryRelativePath = @"tests\Images\ActualOutput"; + + private const string ReferenceOutputDirectoryRelativePath = @"tests\Images\External\ReferenceOutput"; + + private const string ToolsDirectoryRelativePath = @"tests\Images\External\tools"; + + private static Lazy solutionDirectoryFullPath = new Lazy(GetSolutionDirectoryFullPathImpl); + + private static Lazy runsOnCi = new Lazy( + () => + { + bool isCi; + return Boolean.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi; + }); + + // ReSharper disable once InconsistentNaming + /// + /// Gets a value indicating whether test execution runs on CI. + /// + internal static bool RunsOnCI => runsOnCi.Value; + + internal static string SolutionDirectoryFullPath => solutionDirectoryFullPath.Value; + + private static string GetSolutionDirectoryFullPathImpl() + { + string assemblyLocation = typeof(TestEnvironment).GetTypeInfo().Assembly.Location; + + var assemblyFile = new FileInfo(assemblyLocation); + + DirectoryInfo directory = assemblyFile.Directory; + + while (!directory.EnumerateFiles(ImageSharpSolutionFileName).Any()) + { + try + { + directory = directory.Parent; + } + catch (Exception ex) + { + throw new Exception( + $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", + ex); + } + if (directory == null) + { + throw new Exception($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); + } + } + + return directory.FullName; + } + + private static string GetFullPath(string relativePath) => + Path.Combine(SolutionDirectoryFullPath, relativePath) + .Replace('\\', Path.DirectorySeparatorChar); + + /// + /// Gets the correct full path to the Input Images directory. + /// + internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); + + /// + /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) + /// + internal static string ActualOutputDirectoryFullPath => GetFullPath(ActualOutputDirectoryRelativePath); + + /// + /// Gets the correct full path to the Expected Output directory. (To compare the test results to.) + /// + internal static string ReferenceOutputDirectoryFullPath => GetFullPath(ReferenceOutputDirectoryRelativePath); + + internal static string ToolsDirectoryFullPath => GetFullPath(ToolsDirectoryRelativePath); + + internal static string GetReferenceOutputFileName(string actualOutputFileName) => + actualOutputFileName.Replace("ActualOutput", @"External\ReferenceOutput").Replace('\\', Path.DirectorySeparatorChar); + + internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + internal static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + /// + /// Creates the image output directory. + /// + /// The path. + /// The path parts. + /// + /// The . + /// + internal static string CreateOutputDirectory(string path, params string[] pathParts) + { + path = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, path); + + if (pathParts != null && pathParts.Length > 0) + { + path = Path.Combine(path, Path.Combine(pathParts)); + } + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + return path; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index bb97daaa47..2b3cb1bcc3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -1,16 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; + using System.Numerics; + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Memory; - using ImageSharp.PixelFormats; + using Xunit; public static class TestImageExtensions { @@ -20,42 +27,220 @@ namespace ImageSharp.Tests /// The pixel format /// The image /// The image provider - /// The settings + /// Details to be concatenated to the test output file, describing the parameters of the test. /// The extension - public static Image DebugSave(this Image image, ITestImageProvider provider, object settings = null, string extension = "png") + /// A boolean indicating whether to append the pixel type to the output file name. + public static Image DebugSave( + this Image image, + ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) where TPixel : struct, IPixel { - if (bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi) + if (TestEnvironment.RunsOnCI) { return image; } // We are running locally then we want to save it out - string tag = null; - string s = settings as string; + provider.Utility.SaveTestOutputFile( + image, + extension, + testOutputDetails: testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName); + return image; + } + + /// + /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. + /// The output file should be named identically to the output produced by . + /// + /// The pixel format + /// The image + /// The image provider + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension + /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. + /// A boolean indicating whether to append the pixel type to the output file name. + /// + public static Image CompareToReferenceOutput( + this Image image, + ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + return CompareToReferenceOutput( + image, + provider, + ImageComparer.Tolerant(), + testOutputDetails, + extension, + grayscale, + appendPixelTypeToFileName); + } + + /// + /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. + /// The output file should be named identically to the output produced by . + /// + /// The pixel format + /// The image + /// The image provider + /// The to use + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension + /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. + /// A boolean indicating whether to append the pixel type to the output file name. + /// + public static Image CompareToReferenceOutput( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image referenceImage = GetReferenceOutputImage( + provider, + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + comparer.VerifySimilarity(referenceImage, image); + } + + return image; + } + + public static Image GetReferenceOutputImage(this ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + + if (!File.Exists(referenceOutputFile)) + { + throw new Exception("Reference output file missing: " + referenceOutputFile); + } + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); + + return Image.Load(referenceOutputFile, decoder); + } + + public static IEnumerable GetReferenceOutputSimilarityReports( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image referenceImage = provider.GetReferenceOutputImage( + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + return comparer.CompareImages(referenceImage, image); + } + } + + public static Image ComparePixelBufferTo( + this Image image, + Span expectedPixels) + where TPixel : struct, IPixel + { + Span actual = image.GetPixelSpan(); + + Assert.True(expectedPixels.Length == actual.Length, "Buffer sizes are not equal!"); - if (s != null) + for (int i = 0; i < expectedPixels.Length; i++) { - tag = s; + Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!"); } - else if (settings != null) + + return image; + } + + public static ImageFrame ComparePixelBufferTo( + this ImageFrame image, + Span expectedPixels) + where TPixel : struct, IPixel + { + Span actual = image.GetPixelSpan(); + + Assert.True(expectedPixels.Length == actual.Length, "Buffer sizes are not equal!"); + + for (int i = 0; i < expectedPixels.Length; i++) { - Type type = settings.GetType(); - TypeInfo info = type.GetTypeInfo(); - if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) - { - tag = settings.ToString(); - } - else - { - IEnumerable properties = settings.GetType().GetRuntimeProperties(); + Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!"); + } - tag = string.Join("_", properties.ToDictionary(x => x.Name, x => x.GetValue(settings)).Select(x => $"{x.Key}-{x.Value}")); - } + return image; + } + + public static Image CompareToOriginal( + this Image image, + ITestImageProvider provider) + where TPixel : struct, IPixel + { + return CompareToOriginal(image, provider, ImageComparer.Tolerant()); + } + + public static Image CompareToOriginal( + this Image image, + ITestImageProvider provider, + ImageComparer comparer) + where TPixel : struct, IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + var testFile = TestFile.Create(path); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + IImageFormat format = TestEnvironment.GetImageFormat(path); + IImageDecoder defaultDecoder = Configuration.Default.FindDecoder(format); + + //if (referenceDecoder.GetType() == defaultDecoder.GetType()) + //{ + // throw new InvalidOperationException($"Can't use CompareToOriginal(): no actual reference decoder registered for {format.Name}"); + //} + + using (var original = Image.Load(testFile.Bytes, referenceDecoder)) + { + comparer.VerifySimilarity(original, image); + } + + return image; + } + + internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) + { + var image = new Image(buffer.Width, buffer.Height); + + Span pixels = image.Frames.RootFrame.GetPixelSpan(); + + for (int i = 0; i < buffer.Length; i++) + { + float value = buffer[i] * scale; + var v = new Vector4(value, value, value, 1f); + pixels[i].PackFromVector4(v); } - provider.Utility.SaveTestOutputFile(image, extension, tag: tag); return image; } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index 852bc587d0..1eb0aafff0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -1,10 +1,13 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Collections.Generic; using System.Text; -using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; -namespace ImageSharp.Tests.TestUtilities +namespace SixLabors.ImageSharp.Tests.TestUtilities { public class TestPixel : IXunitSerializable where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Tests/TestUtilities/TestType.cs b/tests/ImageSharp.Tests/TestUtilities/TestType.cs new file mode 100644 index 0000000000..788a0543ab --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestType.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + public class TestType : IXunitSerializable + { + public TestType() + { + } + + public void Deserialize(IXunitSerializationInfo info) + { + } + + public void Serialize(IXunitSerializationInfo info) + { + } + + public override string ToString() + { + return $"Type<{typeof(T).Name}>"; + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs deleted file mode 100644 index e42cbe1934..0000000000 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs +++ /dev/null @@ -1,138 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Reflection; - - using ImageSharp.PixelFormats; - - /// - /// Extension methods for TestUtilities - /// - public static class TestUtilityExtensions - { - private static readonly Dictionary ClrTypes2PixelTypes = new Dictionary(); - - private static readonly Assembly ImageSharpAssembly = typeof(Rgba32).GetTypeInfo().Assembly; - - private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary(); - - private static readonly PixelTypes[] AllConcretePixelTypes = GetAllPixelTypes() - .Except(new[] { PixelTypes.Undefined, PixelTypes.All }) - .ToArray(); - - static TestUtilityExtensions() - { - // Add Rgba32 Our default. - Type defaultPixelFormatType = typeof(Rgba32); - PixelTypes2ClrTypes[PixelTypes.Rgba32] = defaultPixelFormatType; - ClrTypes2PixelTypes[defaultPixelFormatType] = PixelTypes.Rgba32; - - // Add PixelFormat types - string nameSpace = typeof(Alpha8).FullName; - nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(Alpha8).Name.Length - 1); - foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.Rgba32)) - { - string typeName = $"{nameSpace}.{pt}"; - Type t = ImageSharpAssembly.GetType(typeName); - PixelTypes2ClrTypes[pt] = t ?? throw new InvalidOperationException($"Could not find: {typeName}"); - ClrTypes2PixelTypes[t] = pt; - } - } - - public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; - - public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true) - where TPixel : struct, IPixel - { - if (a.Width != b.Width || a.Height != b.Height) - { - return false; - } - - byte[] bytesA = new byte[3]; - byte[] bytesB = new byte[3]; - - using (PixelAccessor pixA = a.Lock()) - { - using (PixelAccessor pixB = b.Lock()) - { - for (int y = 0; y < a.Height; y++) - { - for (int x = 0; x < a.Width; x++) - { - TPixel ca = pixA[x, y]; - TPixel cb = pixB[x, y]; - - if (compareAlpha) - { - if (!ca.Equals(cb)) - { - return false; - } - } - else - { - ca.ToXyzBytes(bytesA, 0); - cb.ToXyzBytes(bytesB, 0); - - if (bytesA[0] != bytesB[0] || - bytesA[1] != bytesB[1] || - bytesA[2] != bytesB[2]) - { - return false; - } - } - } - } - } - } - - return true; - } - - public static string ToCsv(this IEnumerable items, string separator = ",") - { - return string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o))); - } - - public static Type ToType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; - - /// - /// Returns the enumerations for the given type. - /// - /// - /// - public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; - - public static IEnumerable> ExpandAllTypes(this PixelTypes pixelTypes) - { - if (pixelTypes == PixelTypes.Undefined) - { - return Enumerable.Empty>(); - } - else if (pixelTypes == PixelTypes.All) - { - // TODO: Need to return unknown types here without forcing CLR to load all types in ImageSharp assembly - return PixelTypes2ClrTypes; - } - - return AllConcretePixelTypes - .Where(pt => pixelTypes.HasFlag(pt)) - .Select(pt => new KeyValuePair(pt, pt.ToType())); - } - - /// - /// Enumerate all available -s - /// - /// The pixel types - internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes)); - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs new file mode 100644 index 0000000000..dd033ae7c8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -0,0 +1,215 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; + + /// + /// Various utility and extension methods. + /// + public static class TestUtils + { + private static readonly Dictionary ClrTypes2PixelTypes = new Dictionary(); + + private static readonly Assembly ImageSharpAssembly = typeof(Rgba32).GetTypeInfo().Assembly; + + private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary(); + + private static readonly PixelTypes[] AllConcretePixelTypes = GetAllPixelTypes() + .Except(new[] { PixelTypes.Undefined, PixelTypes.All }) + .ToArray(); + + static TestUtils() + { + // Add Rgba32 Our default. + Type defaultPixelFormatType = typeof(Rgba32); + PixelTypes2ClrTypes[PixelTypes.Rgba32] = defaultPixelFormatType; + ClrTypes2PixelTypes[defaultPixelFormatType] = PixelTypes.Rgba32; + + // Add PixelFormat types + string nameSpace = typeof(Alpha8).FullName; + nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(Alpha8).Name.Length - 1); + foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.Rgba32)) + { + string typeName = $"{nameSpace}.{pt}"; + Type t = ImageSharpAssembly.GetType(typeName); + PixelTypes2ClrTypes[pt] = t ?? throw new InvalidOperationException($"Could not find: {typeName}"); + ClrTypes2PixelTypes[t] = pt; + } + } + + public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; + + public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true) + where TPixel : struct, IPixel + { + if (a.Width != b.Width || a.Height != b.Height) + { + return false; + } + + var rgb1 = default(Rgb24); + var rgb2 = default(Rgb24); + + using (PixelAccessor pixA = a.Lock()) + { + using (PixelAccessor pixB = b.Lock()) + { + for (int y = 0; y < a.Height; y++) + { + for (int x = 0; x < a.Width; x++) + { + TPixel ca = pixA[x, y]; + TPixel cb = pixB[x, y]; + + if (compareAlpha) + { + if (!ca.Equals(cb)) + { + return false; + } + } + else + { + ca.ToRgb24(ref rgb1); + cb.ToRgb24(ref rgb2); + + if (rgb1.R != rgb2.R || + rgb1.G != rgb2.G || + rgb1.B != rgb2.B) + { + return false; + } + } + } + } + } + } + + return true; + } + + public static string ToCsv(this IEnumerable items, string separator = ",") + { + return string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o))); + } + + public static Type GetClrType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; + + /// + /// Returns the enumerations for the given type. + /// + /// + /// + public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; + + + + public static IEnumerable> ExpandAllTypes(this PixelTypes pixelTypes) + { + if (pixelTypes == PixelTypes.Undefined) + { + return Enumerable.Empty>(); + } + else if (pixelTypes == PixelTypes.All) + { + // TODO: Need to return unknown types here without forcing CLR to load all types in ImageSharp assembly + return PixelTypes2ClrTypes; + } + + var result = new Dictionary(); + foreach (PixelTypes pt in AllConcretePixelTypes) + { + if (pixelTypes.HasAll(pt)) + { + result[pt] = pt.GetClrType(); + } + } + return result; + } + + internal static bool HasAll(this PixelTypes pixelTypes, PixelTypes flagsToCheck) => + (pixelTypes & flagsToCheck) == flagsToCheck; + + /// + /// Enumerate all available -s + /// + /// The pixel types + internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes)); + + + /// + /// Utility for testing image processor extension methods: + /// 1. Run a processor defined by 'process' + /// 2. Run 'DebugSave()' to save the output locally + /// 3. Run 'CompareToReferenceOutput()' to compare the results to the expected output + /// + /// The + /// The image processing method to test. (As a delegate) + /// The value to append to the test output. + /// The custom image comparer to use + internal static void RunValidatingProcessorTest( + this TestImageProvider provider, + Action> process, + object testOutputDetails = null, + ImageComparer comparer = null) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(process); + image.DebugSave(provider, testOutputDetails); + + // TODO: Investigate the cause of pixel inaccuracies under Linux + if (TestEnvironment.IsWindows) + { + image.CompareToReferenceOutput(provider, testOutputDetails); + } + } + } + + /// + /// Same as but with an additional parameter passed to 'process' + /// + internal static void RunRectangleConstrainedValidatingProcessorTest( + this TestImageProvider provider, + Action, Rectangle> process, + object testOutputDetails = null, + ImageComparer comparer = null) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var bounds = new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2); + image.Mutate(x => process(x, bounds)); + image.DebugSave(provider, testOutputDetails); + image.CompareToReferenceOutput(provider, testOutputDetails); + } + } + + /// + /// Same as but without the 'CompareToReferenceOutput()' step. + /// + internal static void RunProcessorTest( + this TestImageProvider provider, + Action> process, + object testOutputDetails = null) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(process); + image.DebugSave(provider, testOutputDetails); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs index beb3fcd970..3916f189cc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs @@ -1,11 +1,14 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Collections.Generic; using System.Numerics; using System.Text; -using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; -namespace ImageSharp.Tests.TestUtilities +namespace SixLabors.ImageSharp.Tests.TestUtilities { public class TestVector4 : IXunitSerializable { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/GroupOutputTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/GroupOutputTests.cs new file mode 100644 index 0000000000..be12678c88 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/GroupOutputTests.cs @@ -0,0 +1,30 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using System.IO; + + using SixLabors.ImageSharp.PixelFormats; + + using Xunit; + + [GroupOutput("Foo")] + public class GroupOutputTests + { + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] + public void OutputSubfolderName_ValueIsTakeFromGroupOutputAttribute(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Equal("Foo", provider.Utility.OutputSubfolderName); + } + + [Theory] + [WithBlankImages(1,1, PixelTypes.Rgba32)] + public void GetTestOutputDir_ShouldDefineSubfolder(TestImageProvider provider) + where TPixel : struct, IPixel + { + string expected = $"{Path.DirectorySeparatorChar}Foo{Path.DirectorySeparatorChar}"; + Assert.Contains(expected, provider.Utility.GetTestOutputDir()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs new file mode 100644 index 0000000000..3f8ec05568 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs @@ -0,0 +1,185 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using System.Collections.Generic; + using System.Linq; + + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Moq; + + using SixLabors.Primitives; + + using Xunit; + using Xunit.Abstractions; + + public class ImageComparerTests + { + public ImageComparerTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + [Theory] + [WithTestPatternImages(100,100,PixelTypes.Rgba32, 0.0001f, 1)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0, 0)] + public void TolerantImageComparer_ApprovesPerfectSimilarity( + TestImageProvider provider, + float imageTheshold, + int pixelThreshold) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + var comparer = ImageComparer.Tolerant(imageTheshold, pixelThreshold); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(110, 110, PixelTypes.Rgba32)] + public void TolerantImageComparer_ApprovesSimilarityBelowTolerance(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 1); + + var comparer = ImageComparer.Tolerant(); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TolerantImageComparer_DoesNotApproveSimilarityAboveTolerance(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + byte perChannelChange = 2; + ImagingTestCaseUtility.ModifyPixel(clone, 3, 1, perChannelChange); + + var comparer = ImageComparer.Tolerant(); + + ImageDifferenceIsOverThresholdException ex = Assert.ThrowsAny( + () => + { + comparer.VerifySimilarity(image, clone); + }); + PixelDifference diff = ex.Reports.Single().Differences.Single(); + Assert.Equal(new Point(3, 1), diff.Position); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TolerantImageComparer_TestPerPixelThreshold(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 10); + ImagingTestCaseUtility.ModifyPixel(clone, 1, 0, 10); + ImagingTestCaseUtility.ModifyPixel(clone, 2, 0, 10); + + var comparer = ImageComparer.Tolerant(perPixelManhattanThreshold: 42); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 99, 100)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 100, 99)] + public void VerifySimilarity_ThrowsOnSizeMismatch(TestImageProvider provider, int w, int h) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone(ctx => ctx.Resize(w, h))) + { + ImageDimensionsMismatchException ex = Assert.ThrowsAny( + () => + { + ImageComparer comparer = Mock.Of(); + comparer.VerifySimilarity(image, clone); + }); + this.Output.WriteLine(ex.Message); + } + } + } + + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void VerifySimilarity_WhenAnImageFrameIsDifferent_Reports(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone.Frames[0], 42, 43, 1); + + IEnumerable reports = ImageComparer.Exact.CompareImages(image, clone); + + PixelDifference difference = reports.Single().Differences.Single(); + Assert.Equal(new Point(42, 43), difference.Position); + } + } + } + + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void ExactComparer_ApprovesExactEquality(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ExactImageComparer.Instance.CompareImages(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void ExactComparer_DoesNotTolerateAnyPixelDifference(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 42, 24, 1); + ImagingTestCaseUtility.ModifyPixel(clone, 7, 93, 1); + + IEnumerable reports = ExactImageComparer.Instance.CompareImages(image, clone); + + this.Output.WriteLine(reports.Single().ToString()); + PixelDifference[] differences = reports.Single().Differences; + Assert.Equal(2, differences.Length); + Assert.Contains(differences, d => d.Position == new Point(42, 24)); + Assert.Contains(differences, d => d.Position == new Point(7, 93)); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs new file mode 100644 index 0000000000..dde34fcc43 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs @@ -0,0 +1,132 @@ +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + + using Xunit; + using Xunit.Abstractions; + + public class ReferenceCodecTests + { + private ITestOutputHelper Output { get; } + + public ReferenceCodecTests(ITestOutputHelper output) + { + this.Output = output; + } + + [Theory] + [WithTestPatternImages(20, 20, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void ToSystemDrawingBitmap(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.ToSystemDrawingBitmap(image)) + { + string fileName = provider.Utility.GetTestOutputFileName("png"); + sdBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); + } + } + } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void FromFromArgb32SystemDrawingBitmap(TestImageProvider dummyProvider) + where TPixel : struct, IPixel + { + string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); + + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image image = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sdBitmap)) + { + image.DebugSave(dummyProvider); + } + } + } + + private static string SavePng(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + using (Image sourceImage = provider.GetImage()) + { + if (pngColorType != PngColorType.RgbWithAlpha) + { + sourceImage.Mutate(c => c.Opacity(1)); + } + + var encoder = new PngEncoder() { PngColorType = pngColorType }; + return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FromFromArgb32SystemDrawingBitmap2(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (TestEnvironment.IsLinux) return; + + string path = SavePng(provider, PngColorType.RgbWithAlpha); + + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image original = provider.GetImage()) + using (Image resaved = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sdBitmap)) + { + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); + } + } + } + + [Theory(Skip = "Doesen't work yet :(")] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FromFromRgb24SystemDrawingBitmap2(TestImageProvider provider) + where TPixel : struct, IPixel + { + string path = SavePng(provider, PngColorType.Rgb); + + using (Image original = provider.GetImage()) + { + original.Mutate(c => c.Opacity(1)); + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image resaved = SystemDrawingBridge.FromFromRgb24SystemDrawingBitmap(sdBitmap)) + { + resaved.Mutate(c => c.Opacity(1)); + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); + } + } + } + } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void OpenWithReferenceDecoder(TestImageProvider dummyProvider) + where TPixel : struct, IPixel + { + string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); + using (Image image = Image.Load(path, SystemDrawingReferenceDecoder.Instance)) + { + image.DebugSave(dummyProvider); + } + } + + [Theory] + [WithTestPatternImages(20, 20, PixelTypes.Rgba32 | PixelTypes.Argb32)] + public void SaveWithReferenceEncoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "png", SystemDrawingReferenceEncoder.Png); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs new file mode 100644 index 0000000000..25584727a6 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -0,0 +1,115 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; +using Xunit; +using Xunit.Abstractions; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats.Bmp; + using SixLabors.ImageSharp.Formats.Png; + + public class TestEnvironmentTests + { + public TestEnvironmentTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + private void CheckPath(string path) + { + this.Output.WriteLine(path); + Assert.True(Directory.Exists(path)); + } + + [Fact] + public void SolutionDirectoryFullPath() + { + this.CheckPath(TestEnvironment.SolutionDirectoryFullPath); + } + + [Fact] + public void InputImagesDirectoryFullPath() + { + this.CheckPath(TestEnvironment.InputImagesDirectoryFullPath); + } + + [Fact] + public void ExpectedOutputDirectoryFullPath() + { + this.CheckPath(TestEnvironment.ReferenceOutputDirectoryFullPath); + } + + [Fact] + public void GetReferenceOutputFileName() + { + string actual = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, @"foo\bar\lol.jpeg"); + string expected = TestEnvironment.GetReferenceOutputFileName(actual); + + this.Output.WriteLine(expected); + Assert.Contains(TestEnvironment.ReferenceOutputDirectoryFullPath, expected); + } + + [Theory] + [InlineData("lol/foo.png", typeof(SystemDrawingReferenceEncoder))] + [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceEncoder))] + [InlineData("lol/Baz.JPG", typeof(JpegEncoder))] + [InlineData("lol/Baz.gif", typeof(GifEncoder))] + public void GetReferenceEncoder_ReturnsCorrectEncoders_Windows(string fileName, Type expectedEncoderType) + { + if (TestEnvironment.IsLinux) return; + + IImageEncoder encoder = TestEnvironment.GetReferenceEncoder(fileName); + Assert.IsType(expectedEncoderType, encoder); + } + + [Theory] + [InlineData("lol/foo.png", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] + [InlineData("lol/Baz.gif", typeof(GifDecoder))] + public void GetReferenceDecoder_ReturnsCorrectEncoders_Windows(string fileName, Type expectedDecoderType) + { + if (TestEnvironment.IsLinux) return; + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); + Assert.IsType(expectedDecoderType, decoder); + } + + [Theory] + [InlineData("lol/foo.png", typeof(PngEncoder))] + [InlineData("lol/Rofl.bmp", typeof(BmpEncoder))] + [InlineData("lol/Baz.JPG", typeof(JpegEncoder))] + [InlineData("lol/Baz.gif", typeof(GifEncoder))] + public void GetReferenceEncoder_ReturnsCorrectEncoders_Linux(string fileName, Type expectedEncoderType) + { + if (!TestEnvironment.IsLinux) return; + + IImageEncoder encoder = TestEnvironment.GetReferenceEncoder(fileName); + Assert.IsType(expectedEncoderType, encoder); + } + + [Theory] + [InlineData("lol/foo.png", typeof(PngDecoder))] + [InlineData("lol/Rofl.bmp", typeof(BmpDecoder))] + [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] + [InlineData("lol/Baz.gif", typeof(GifDecoder))] + public void GetReferenceDecoder_ReturnsCorrectEncoders_Linux(string fileName, Type expectedDecoderType) + { + if (!TestEnvironment.IsLinux) return; + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); + Assert.IsType(expectedDecoderType, decoder); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageExtensionsTests.cs new file mode 100644 index 0000000000..45ac2d6cca --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageExtensionsTests.cs @@ -0,0 +1,109 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using System; + + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Moq; + + using Xunit; + + public class TestImageExtensionsTests + { + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_WhenReferenceOutputMatches_ShouldNotThrow( + TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_WhenReferenceOutputDoesNotMatch_Throws( + TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.ThrowsAny(() => image.CompareToReferenceOutput(provider)); + } + } + + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_DoNotAppendPixelType( + TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DebugSave(provider, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(provider, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_WhenReferenceFileMissing_Throws(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.ThrowsAny(() => image.CompareToReferenceOutput(provider)); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void CompareToOriginal_WhenSimilar(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + clone.CompareToOriginal(provider, ImageComparer.Exact); + } + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void CompareToOriginal_WhenDifferent_Throws(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + ImagingTestCaseUtility.ModifyPixel(image, 3, 1, 1); + + Assert.ThrowsAny( + () => + { + image.CompareToOriginal(provider, ImageComparer.Exact); + }); + } + } + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void CompareToOriginal_WhenInputIsNotFromFile_Throws(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.ThrowsAny( + () => + { + image.CompareToOriginal(provider, Mock.Of()); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index c01babb632..f0adeb7534 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; - using ImageSharp.PixelFormats; +using System.Collections.Concurrent; +using System.IO; - using Xunit; - using Xunit.Abstractions; +using SixLabors.ImageSharp.Formats; +namespace SixLabors.ImageSharp.Tests +{ public class TestImageProviderTests { public TestImageProviderTests(ITestOutputHelper output) @@ -21,6 +22,14 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] + public void NoOutputSubfolderIsPresentByDefault(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Empty(provider.Utility.OutputSubfolderName); + } + [Theory] [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) @@ -50,25 +59,13 @@ namespace ImageSharp.Tests [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] - [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] + [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) where TPixel : struct, IPixel { Assert.Equal(expected, provider.PixelType); } - [Theory] - [WithBlankImages(1, 1, PixelTypes.Rgba32)] - [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] - public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass( - TestImageProvider provider) - where TPixel : struct, IPixel - { - Image img = provider.GetImage(); - - Assert.IsType>(img); - } - [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] @@ -85,6 +82,155 @@ namespace ImageSharp.Tests this.Output.WriteLine(fn); } + private class TestDecoder : IImageDecoder + { + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + // Couldn't make xUnit happy without this hackery: + + private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + + private string callerName = null; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + private static readonly object Monitor = new object(); + + public static void DoTestThreadSafe(Action action) + { + lock (Monitor) + { + action(); + } + } + } + + + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); + + TestDecoder.DoTestThreadSafe( + () => + { + string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); + + var decoder = new TestDecoder(); + decoder.InitCaller(testName); + + provider.GetImage(decoder); + Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); + + provider.GetImage(decoder); + Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); + }); + } + + private class TestDecoderWithParameters : IImageDecoder + { + public string Param1 { get; set; } + + public int Param2 { get; set; } + + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + + private string callerName = null; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + private static readonly object Monitor = new object(); + + public static void DoTestThreadSafe(Action action) + { + lock (Monitor) + { + action(); + } + } + } + + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); + + TestDecoderWithParameters.DoTestThreadSafe( + () => + { + string testName = + nameof(this.GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual); + + var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; + decoder1.InitCaller(testName); + + var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; + decoder2.InitCaller(testName); + + provider.GetImage(decoder1); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + + provider.GetImage(decoder2); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + }); + } + + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); + + TestDecoderWithParameters.DoTestThreadSafe( + () => + { + string testName = + nameof(this.GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual); + + var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 }; + decoder1.InitCaller(testName); + + var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 }; + decoder2.InitCaller(testName); + + provider.GetImage(decoder1); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + + provider.GetImage(decoder2); + Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName)); + }); + } + + public static string[] AllBmpFiles => TestImages.Bmp.All; [Theory] @@ -106,7 +252,7 @@ namespace ImageSharp.Tests Assert.Equal(10, img.Width); Assert.Equal(20, img.Height); - byte[] colors = new byte[4]; + var rgba = default(Rgba32); using (PixelAccessor pixels = img.Lock()) { @@ -114,12 +260,12 @@ namespace ImageSharp.Tests { for (int x = 0; x < pixels.Width; x++) { - pixels[x, y].ToXyzwBytes(colors, 0); + pixels[x, y].ToRgba32(ref rgba); - Assert.Equal(255, colors[0]); - Assert.Equal(100, colors[1]); - Assert.Equal(50, colors[2]); - Assert.Equal(200, colors[3]); + Assert.Equal(255, rgba.R); + Assert.Equal(100, rgba.G); + Assert.Equal(50, rgba.B); + Assert.Equal(200, rgba.A); } } } @@ -131,10 +277,10 @@ namespace ImageSharp.Tests /// /// /// - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - return factory.CreateImage(3, 3); + return new Image(3, 3); } [Theory] @@ -152,12 +298,12 @@ namespace ImageSharp.Tests } public static readonly TheoryData BasicData = new TheoryData() - { - TestImageProvider.Blank(10, 20), - TestImageProvider.Blank( - 10, - 20), - }; + { + TestImageProvider.Blank(10, 20), + TestImageProvider.Blank( + 10, + 20), + }; [Theory] [MemberData(nameof(BasicData))] @@ -170,12 +316,10 @@ namespace ImageSharp.Tests } public static readonly TheoryData FileData = new TheoryData() - { - TestImageProvider.File( - TestImages.Bmp.Car), - TestImageProvider.File( - TestImages.Bmp.F) - }; + { + TestImageProvider.File(TestImages.Bmp.Car), + TestImageProvider.File(TestImages.Bmp.F) + }; [Theory] [MemberData(nameof(FileData))] @@ -190,4 +334,4 @@ namespace ImageSharp.Tests Assert.True(img.Width * img.Height > 0); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 437c295b9c..fe5af24d26 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -1,21 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Reflection; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; - using Xunit; - using Xunit.Abstractions; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public class TestUtilityExtensionsTests { public TestUtilityExtensionsTests(ITestOutputHelper output) @@ -25,10 +21,10 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + var image = new Image(10, 10); using (PixelAccessor pixels = image.Lock()) { @@ -36,10 +32,10 @@ namespace ImageSharp.Tests { for (int j = 0; j < 10; j++) { - Vector4 v = new Vector4(i, j, 0, 1); + var v = new Vector4(i, j, 0, 1); v /= 10; - TPixel color = default(TPixel); + var color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -50,16 +46,6 @@ namespace ImageSharp.Tests return image; } - [Fact] - public void Baz() - { - Type type = typeof(Rgba32).GetTypeInfo().Assembly.GetType("ImageSharp.Rgba32"); - this.Output.WriteLine(type.ToString()); - - Type fake = typeof(Rgba32).GetTypeInfo().Assembly.GetType("ImageSharp.dsaada_DASqewrr"); - Assert.Null(fake); - } - [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, true)] [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, false)] @@ -67,8 +53,7 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { Image a = provider.GetImage(); - Image b = provider.GetImage(); - b = b.OilPaint(3, 2); + Image b = provider.GetImage(x=>x.OilPaint(3, 2)); Assert.False(a.IsEquivalentTo(b, compareAlpha)); } @@ -89,10 +74,9 @@ namespace ImageSharp.Tests [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] [InlineData(PixelTypes.Argb32, typeof(Argb32))] [InlineData(PixelTypes.HalfVector4, typeof(HalfVector4))] - [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] public void ToType(PixelTypes pt, Type expectedType) { - Assert.Equal(pt.ToType(), expectedType); + Assert.Equal(pt.GetClrType(), expectedType); } [Theory] @@ -112,7 +96,7 @@ namespace ImageSharp.Tests } [Fact] - public void ToTypes() + public void ExpandAllTypes_1() { PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.HalfVector2 | PixelTypes.Rgba32; @@ -126,12 +110,26 @@ namespace ImageSharp.Tests AssertContainsPixelType(PixelTypes.Rgba32, expanded); } + [Fact] + public void ExpandAllTypes_2() + { + PixelTypes pixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + + IEnumerable> expanded = pixelTypes.ExpandAllTypes(); + + Assert.Equal(3, expanded.Count()); + + AssertContainsPixelType(PixelTypes.Rgba32, expanded); + AssertContainsPixelType(PixelTypes.Bgra32, expanded); + AssertContainsPixelType(PixelTypes.RgbaVector, expanded); + } + [Fact] public void ToTypes_All() { KeyValuePair[] expanded = PixelTypes.All.ExpandAllTypes().ToArray(); - Assert.True(expanded.Length >= TestUtilityExtensions.GetAllPixelTypes().Length - 2); + Assert.True(expanded.Length >= TestUtils.GetAllPixelTypes().Length - 2); AssertContainsPixelType(PixelTypes.Rgba32, expanded); AssertContainsPixelType(PixelTypes.Rgba32, expanded); } diff --git a/tests/ImageSharp.Tests/VectorAssert.cs b/tests/ImageSharp.Tests/VectorAssert.cs index ad26963d43..402d066555 100644 --- a/tests/ImageSharp.Tests/VectorAssert.cs +++ b/tests/ImageSharp.Tests/VectorAssert.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; // ReSharper disable MemberHidesStaticFromOuterClass -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Generic; - using System.Numerics; - using ImageSharp; - using ImageSharp.PixelFormats; - using Xunit; - /// /// Class to perform simple image comparisons. /// diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index df1c3d50d0..cbaa8f4325 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,3 +1,4 @@ { - "methodDisplay": "method" + "methodDisplay": "method", + "diagnosticMessages": true } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External new file mode 160000 index 0000000000..376605e05b --- /dev/null +++ b/tests/Images/External @@ -0,0 +1 @@ +Subproject commit 376605e05bb704d425b2d17bf5b310f5376da22e diff --git a/tests/Images/Input/Bmp/BITMAPV5HEADER.bmp b/tests/Images/Input/Bmp/BITMAPV5HEADER.bmp new file mode 100644 index 0000000000..1ab56bb007 --- /dev/null +++ b/tests/Images/Input/Bmp/BITMAPV5HEADER.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d99f80a05e0ddb6309c84846dfac7d2ef4e9cdda6ed437646bc0edfee7d3130 +size 174026 diff --git a/tests/Images/Input/Bmp/BitmapCoreHeaderQR.bmp b/tests/Images/Input/Bmp/BitmapCoreHeaderQR.bmp new file mode 100644 index 0000000000..4c2f26da73 --- /dev/null +++ b/tests/Images/Input/Bmp/BitmapCoreHeaderQR.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04709a3b7e9a73e87f12c5c63f66bc608e3cd61c23fcd38629bb8638bbb1b4de +size 2580 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp b/tests/Images/Input/Bmp/Car.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp rename to tests/Images/Input/Bmp/Car.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/F.bmp b/tests/Images/Input/Bmp/F.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/F.bmp rename to tests/Images/Input/Bmp/F.bmp diff --git a/tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp b/tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp new file mode 100644 index 0000000000..f773daba7e --- /dev/null +++ b/tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4113b0ec1a834b902efa18fc8dc050eda1e18f9228cbc6b06b45d6337ad22c88 +size 7278 diff --git a/tests/Images/Input/Bmp/RunLengthEncoded.bmp b/tests/Images/Input/Bmp/RunLengthEncoded.bmp new file mode 100644 index 0000000000..7e8d3acd48 --- /dev/null +++ b/tests/Images/Input/Bmp/RunLengthEncoded.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37d9fe070ed05309a4baa81acc1e63c690b5a706d238ca200034d17dd53c8bb5 +size 7278 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/neg_height.bmp b/tests/Images/Input/Bmp/neg_height.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/neg_height.bmp rename to tests/Images/Input/Bmp/neg_height.bmp diff --git a/tests/Images/Input/Bmp/test16-inverted.bmp b/tests/Images/Input/Bmp/test16-inverted.bmp new file mode 100644 index 0000000000..551de69b36 --- /dev/null +++ b/tests/Images/Input/Bmp/test16-inverted.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:474f93277f764b75ec91446fdffb8f290a6073236aed468a65cba5a333707c7d +size 16438 diff --git a/tests/Images/Input/Bmp/test16.bmp b/tests/Images/Input/Bmp/test16.bmp new file mode 100644 index 0000000000..d6c5a67a66 --- /dev/null +++ b/tests/Images/Input/Bmp/test16.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:472193f2506d768c45ec167518cb7da56e0e8707887431db20d1cfa5f61bd235 +size 16438 diff --git a/tests/Images/Input/Bmp/test8-inverted.bmp b/tests/Images/Input/Bmp/test8-inverted.bmp new file mode 100644 index 0000000000..4ec260b660 --- /dev/null +++ b/tests/Images/Input/Bmp/test8-inverted.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:407c948e576330f603b3692c3d37248f8df31067f79e424dba6757066c97845e +size 9270 diff --git a/tests/Images/Input/Bmp/test8.bmp b/tests/Images/Input/Bmp/test8.bmp new file mode 100644 index 0000000000..157c96879a --- /dev/null +++ b/tests/Images/Input/Bmp/test8.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3551fbb385b6d7d54e2f0372f79e5701ca9215e4efdddb429ec5d7942367107f +size 9270 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/cheers.gif b/tests/Images/Input/Gif/cheers.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/cheers.gif rename to tests/Images/Input/Gif/cheers.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/giphy.gif b/tests/Images/Input/Gif/giphy.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/giphy.gif rename to tests/Images/Input/Gif/giphy.gif diff --git a/tests/Images/Input/Gif/issues/issue403_baddescriptorwidth.gif b/tests/Images/Input/Gif/issues/issue403_baddescriptorwidth.gif new file mode 100644 index 0000000000..0dce4b0eec --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue403_baddescriptorwidth.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c108091ffddd87178378656e37a7e975aa69be56376b9105adbbc14fe8d9a010 +size 707660 diff --git a/tests/Images/Input/Gif/issues/issue405_badappextlength252-2.gif b/tests/Images/Input/Gif/issues/issue405_badappextlength252-2.gif new file mode 100644 index 0000000000..98738c5118 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue405_badappextlength252-2.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:151d394c7c57a1696fedc32116514b667ad70c4a2c7d34db7c489c17d5755eed +size 24724 diff --git a/tests/Images/Input/Gif/issues/issue405_badappextlength252.gif b/tests/Images/Input/Gif/issues/issue405_badappextlength252.gif new file mode 100644 index 0000000000..6fbc3713f8 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue405_badappextlength252.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0549f4500693cdf7eac8351d8cd64d1093da9e9eaaed923e8c6f00476e350f43 +size 41721 diff --git a/tests/Images/Input/Gif/kumin.gif b/tests/Images/Input/Gif/kumin.gif new file mode 100644 index 0000000000..31efda7d8c --- /dev/null +++ b/tests/Images/Input/Gif/kumin.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:345556b9a0e064412acc3b2ee87a9226113eb65c0a1791c2f855ac3fa1e6b7ad +size 868269 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif b/tests/Images/Input/Gif/rings.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif rename to tests/Images/Input/Gif/rings.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif b/tests/Images/Input/Gif/trans.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif rename to tests/Images/Input/Gif/trans.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Calliphora.jpg b/tests/Images/Input/Jpg/baseline/Calliphora.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Calliphora.jpg rename to tests/Images/Input/Jpg/baseline/Calliphora.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg b/tests/Images/Input/Jpg/baseline/ExifUndefType.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg rename to tests/Images/Input/Jpg/baseline/ExifUndefType.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Floorplan.jpg b/tests/Images/Input/Jpg/baseline/Floorplan.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Floorplan.jpg rename to tests/Images/Input/Jpg/baseline/Floorplan.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Hiyamugi.jpg b/tests/Images/Input/Jpg/baseline/Hiyamugi.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Hiyamugi.jpg rename to tests/Images/Input/Jpg/baseline/Hiyamugi.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Lake.jpg b/tests/Images/Input/Jpg/baseline/Lake.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Lake.jpg rename to tests/Images/Input/Jpg/baseline/Lake.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Snake.jpg b/tests/Images/Input/Jpg/baseline/Snake.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Snake.jpg rename to tests/Images/Input/Jpg/baseline/Snake.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/badeof.jpg b/tests/Images/Input/Jpg/baseline/badeof.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/badeof.jpg rename to tests/Images/Input/Jpg/baseline/badeof.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/cmyk.jpg b/tests/Images/Input/Jpg/baseline/cmyk.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/cmyk.jpg rename to tests/Images/Input/Jpg/baseline/cmyk.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/exif.jpg b/tests/Images/Input/Jpg/baseline/exif.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/exif.jpg rename to tests/Images/Input/Jpg/baseline/exif.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/gamma_dalai_lama_gray.jpg b/tests/Images/Input/Jpg/baseline/gamma_dalai_lama_gray.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/gamma_dalai_lama_gray.jpg rename to tests/Images/Input/Jpg/baseline/gamma_dalai_lama_gray.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg400jfif.jpg b/tests/Images/Input/Jpg/baseline/jpeg400jfif.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg400jfif.jpg rename to tests/Images/Input/Jpg/baseline/jpeg400jfif.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg b/tests/Images/Input/Jpg/baseline/jpeg420exif.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg rename to tests/Images/Input/Jpg/baseline/jpeg420exif.jpg diff --git a/tests/Images/Input/Jpg/baseline/jpeg420small.jpg b/tests/Images/Input/Jpg/baseline/jpeg420small.jpg new file mode 100644 index 0000000000..8e5d1c7bdb --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/jpeg420small.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac2cbbe4e6240cff33468e780eaf3de8a1800e5b8cdce5a9959268c17d566e92 +size 5276 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg444.jpg b/tests/Images/Input/Jpg/baseline/jpeg444.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg444.jpg rename to tests/Images/Input/Jpg/baseline/jpeg444.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testimgint.jpg b/tests/Images/Input/Jpg/baseline/testimgint.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testimgint.jpg rename to tests/Images/Input/Jpg/baseline/testimgint.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testorig.jpg b/tests/Images/Input/Jpg/baseline/testorig.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testorig.jpg rename to tests/Images/Input/Jpg/baseline/testorig.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/turtle.jpg b/tests/Images/Input/Jpg/baseline/turtle.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/turtle.jpg rename to tests/Images/Input/Jpg/baseline/turtle.jpg diff --git a/tests/Images/Input/Jpg/baseline/ycck.jpg b/tests/Images/Input/Jpg/baseline/ycck.jpg new file mode 100644 index 0000000000..2fe8f0a61d --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/ycck.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33e3546a64df7fa1d528441926421b193e399a83490a6307762fb7eee9640bf0 +size 611572 diff --git a/tests/Images/Input/Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg b/tests/Images/Input/Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg new file mode 100644 index 0000000000..34bc89b2fe --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98c5a53bd06e1a2d6c40614c19fbc360f5ed8b16c83da0c3ee295a0a2e080a00 +size 60927 diff --git a/tests/Images/Input/Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg b/tests/Images/Input/Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg new file mode 100644 index 0000000000..aee0182a6b --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f8b4a062931424bedc6ce9a6b02fb8eec59cd8a386b09a1c7a790a041cbea89 +size 279270 diff --git a/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg b/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg new file mode 100644 index 0000000000..165eb16698 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab21c74db071f88a600984bf39c6c5d0448433c353b719584f60621d4c21dcb1 +size 35601 diff --git a/tests/Images/Input/Jpg/issues/Issue385-BadZigZag-Progressive.jpg b/tests/Images/Input/Jpg/issues/Issue385-BadZigZag-Progressive.jpg new file mode 100644 index 0000000000..963cb3d978 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue385-BadZigZag-Progressive.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1052660c765aaad52f7bbad779778b5d15fdc3c92ff4b114c89e579c749c7f6b +size 388517 diff --git a/tests/Images/Input/Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg b/tests/Images/Input/Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg new file mode 100644 index 0000000000..6e4dc0d0f3 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e90cf157d8e2599598c1bea1b8e2bf262651b004cc6261d87bbb49bd6131034 +size 257401 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/BadEofProgressive.jpg b/tests/Images/Input/Jpg/progressive/BadEofProgressive.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/BadEofProgressive.jpg rename to tests/Images/Input/Jpg/progressive/BadEofProgressive.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/Festzug.jpg b/tests/Images/Input/Jpg/progressive/Festzug.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/Festzug.jpg rename to tests/Images/Input/Jpg/progressive/Festzug.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/fb.jpg b/tests/Images/Input/Jpg/progressive/fb.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/fb.jpg rename to tests/Images/Input/Jpg/progressive/fb.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/progress.jpg b/tests/Images/Input/Jpg/progressive/progress.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/progress.jpg rename to tests/Images/Input/Jpg/progressive/progress.jpg diff --git a/tests/Images/Input/Png/Bike.png b/tests/Images/Input/Png/Bike.png new file mode 100644 index 0000000000..5d5b2b2293 --- /dev/null +++ b/tests/Images/Input/Png/Bike.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66188aa1e0b12e35598a3f1d7abf17aaa2a48b66eb030de5a6e7416237420030 +size 292942 diff --git a/tests/Images/Input/Png/BikeGrayscale.png b/tests/Images/Input/Png/BikeGrayscale.png new file mode 100644 index 0000000000..893aff97ff --- /dev/null +++ b/tests/Images/Input/Png/BikeGrayscale.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c1030c7587bcfea910ac3e7e111f30b3c15366ff4e06f9f2fc81865e9d4124d +size 84783 diff --git a/tests/Images/Input/Png/CalliphoraPartial.png b/tests/Images/Input/Png/CalliphoraPartial.png new file mode 100644 index 0000000000..d8c27fb624 --- /dev/null +++ b/tests/Images/Input/Png/CalliphoraPartial.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ddd702463bf22e52cdf185c0c9c98f97531ffce67700ec48a391f5a9d8f2c97 +size 331346 diff --git a/tests/Images/Input/Png/CalliphoraPartialGrayscale.png b/tests/Images/Input/Png/CalliphoraPartialGrayscale.png new file mode 100644 index 0000000000..9a42449c51 --- /dev/null +++ b/tests/Images/Input/Png/CalliphoraPartialGrayscale.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52ad4d68de5759444b46d9ba2dc9f023f09409cc31d839ceb069e2082b8debe0 +size 98190 diff --git a/tests/Images/Input/Png/SnakeGame.png b/tests/Images/Input/Png/SnakeGame.png new file mode 100644 index 0000000000..1aa3295521 --- /dev/null +++ b/tests/Images/Input/Png/SnakeGame.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6d874f98a08647047ecfc015b39f035ff863713699263251114690be6283a2d +size 6958 diff --git a/tests/Images/Input/Png/banner7-adam.png b/tests/Images/Input/Png/banner7-adam.png new file mode 100644 index 0000000000..b7bedd8884 --- /dev/null +++ b/tests/Images/Input/Png/banner7-adam.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5eb7d02dfce0821a5210ed69c887516a39089baca2989252afd712e41e656586 +size 16488 diff --git a/tests/Images/Input/Png/banner8-index.png b/tests/Images/Input/Png/banner8-index.png new file mode 100644 index 0000000000..075ca688db --- /dev/null +++ b/tests/Images/Input/Png/banner8-index.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6398956c686a1e280c5d29d8301aebee7269296598cebb01454ba0c9e7f279bc +size 2327 diff --git a/tests/Images/Input/Png/big-corrupted-chunk.png b/tests/Images/Input/Png/big-corrupted-chunk.png new file mode 100644 index 0000000000..2d46460fc0 --- /dev/null +++ b/tests/Images/Input/Png/big-corrupted-chunk.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6684985456687682d74b63ad8ef7983f2d6b593a6edc243b1a21c6a64cccf34a +size 9195 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/blur.png b/tests/Images/Input/Png/blur.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/blur.png rename to tests/Images/Input/Png/blur.png diff --git a/tests/Images/Input/Png/bpp1.png b/tests/Images/Input/Png/bpp1.png new file mode 100644 index 0000000000..cbfb46bda7 --- /dev/null +++ b/tests/Images/Input/Png/bpp1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:364e9fac6467570afe2f23e432d4f7df10dd2dd53920d4f2c743ac0f8519f1e0 +size 4403 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png b/tests/Images/Input/Png/chunklength1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png rename to tests/Images/Input/Png/chunklength1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png b/tests/Images/Input/Png/chunklength2.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png rename to tests/Images/Input/Png/chunklength2.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png b/tests/Images/Input/Png/cross.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png rename to tests/Images/Input/Png/cross.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png b/tests/Images/Input/Png/filter0.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png rename to tests/Images/Input/Png/filter0.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png b/tests/Images/Input/Png/filter1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png rename to tests/Images/Input/Png/filter1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png b/tests/Images/Input/Png/filter2.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png rename to tests/Images/Input/Png/filter2.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png b/tests/Images/Input/Png/filter3.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png rename to tests/Images/Input/Png/filter3.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png b/tests/Images/Input/Png/filter4.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png rename to tests/Images/Input/Png/filter4.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png b/tests/Images/Input/Png/filterVar.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png rename to tests/Images/Input/Png/filterVar.png diff --git a/tests/Images/Input/Png/gray_4bpp.png b/tests/Images/Input/Png/gray_4bpp.png new file mode 100644 index 0000000000..ff4f77fe39 --- /dev/null +++ b/tests/Images/Input/Png/gray_4bpp.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7c6d6cbd959e84001e559702c31e313d065c9cc25808c190c4d4a1f564d4357 +size 7396 diff --git a/tests/Images/Input/Png/icon.png b/tests/Images/Input/Png/icon.png new file mode 100644 index 0000000000..bc355712b5 --- /dev/null +++ b/tests/Images/Input/Png/icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18f1eb7c5019153f4a0b2de90e7e0f0521193f003fabd6ac31c2f58c2562ae42 +size 4040 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/indexed.png b/tests/Images/Input/Png/indexed.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/indexed.png rename to tests/Images/Input/Png/indexed.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png b/tests/Images/Input/Png/interlaced.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png rename to tests/Images/Input/Png/interlaced.png diff --git a/tests/Images/Input/Png/kaboom.png b/tests/Images/Input/Png/kaboom.png new file mode 100644 index 0000000000..29f2bbfc15 --- /dev/null +++ b/tests/Images/Input/Png/kaboom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8d493f5472e58b3184d1c6fe3795a731b6cb6de765ae0d27726d69aeff06d6b +size 573925 diff --git a/tests/Images/Input/Png/palette-8bpp.png b/tests/Images/Input/Png/palette-8bpp.png new file mode 100644 index 0000000000..8943fdeb37 --- /dev/null +++ b/tests/Images/Input/Png/palette-8bpp.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7bfbc244f4b0672d6a12a1f73ec062bcf762f229268b99aa4b9ffd8447512471 +size 9171 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pd.png b/tests/Images/Input/Png/pd.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/pd.png rename to tests/Images/Input/Png/pd.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pl.png b/tests/Images/Input/Png/pl.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/pl.png rename to tests/Images/Input/Png/pl.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png b/tests/Images/Input/Png/pp.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png rename to tests/Images/Input/Png/pp.png diff --git a/tests/Images/Input/Png/rgb-48bpp-interlaced.png b/tests/Images/Input/Png/rgb-48bpp-interlaced.png new file mode 100644 index 0000000000..d19d44ea4b --- /dev/null +++ b/tests/Images/Input/Png/rgb-48bpp-interlaced.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79d1705ef6438d90dc145068141d4e32c1cf1cd5cf17c7b90b7ecb12967016bd +size 69952 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp.png b/tests/Images/Input/Png/rgb-48bpp.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp.png rename to tests/Images/Input/Png/rgb-48bpp.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/splash-interlaced.png b/tests/Images/Input/Png/splash-interlaced.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/splash-interlaced.png rename to tests/Images/Input/Png/splash-interlaced.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/splash.png b/tests/Images/Input/Png/splash.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/splash.png rename to tests/Images/Input/Png/splash.png diff --git a/tests/Images/Input/Png/versioning-1_1.png b/tests/Images/Input/Png/versioning-1_1.png new file mode 100644 index 0000000000..0eb37aab87 --- /dev/null +++ b/tests/Images/Input/Png/versioning-1_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce623255656921d491b5c389cd46931fbd6024575b87522c55d67a496dd761f0 +size 22781 diff --git a/tests/Images/Input/Png/versioning-1_2.png b/tests/Images/Input/Png/versioning-1_2.png new file mode 100644 index 0000000000..d402c7fd2d --- /dev/null +++ b/tests/Images/Input/Png/versioning-1_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37bace8aaaa921b757af7d43ae0e91cbe0738942439753c401209215a1acd20d +size 8165 diff --git a/tests/Images/Input/Png/vim16x16_1.png b/tests/Images/Input/Png/vim16x16_1.png new file mode 100644 index 0000000000..a55a1a73da --- /dev/null +++ b/tests/Images/Input/Png/vim16x16_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9f8ecc9936ef3ce54f5b9b2aac816b9539b753236c53e249cde0f2791aa4712 +size 226 diff --git a/tests/Images/Input/Png/vim16x16_2.png b/tests/Images/Input/Png/vim16x16_2.png new file mode 100644 index 0000000000..39d42c88f9 --- /dev/null +++ b/tests/Images/Input/Png/vim16x16_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:491ef70e69f63c2d5b48680faebd5b008267850b426f512878854bb1e971eba0 +size 292 diff --git a/theme/index.cshtml b/theme/index.cshtml deleted file mode 100644 index d3656f800b..0000000000 --- a/theme/index.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -Title: Home ---- -Welcome to the documentation for ImageSharp \ No newline at end of file