diff --git a/.gitattributes b/.gitattributes index b9a9ddd4c3..2cbe4b423a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -62,14 +62,28 @@ # normalize to Windows-style line endings and *.sln text eol=crlf merge=union # treat as binary +*.basis binary *.bmp binary +*.dds binary *.dll binary +*.eot binary *.exe binary *.gif binary *.jpg binary +*.ktx binary +*.pbm binary +*.pdf binary *.png binary +*.ppt binary +*.pptx binary +*.pvr binary *.ttf binary *.snk binary +*.tga binary +*.woff binary +*.woff2 binary +*.xls binary +*.xlsx binary # diff as plain text *.doc diff=astextplain *.docx diff=astextplain @@ -77,6 +91,7 @@ *.pdf diff=astextplain *.pptx diff=astextplain *.rtf diff=astextplain +*.svg diff=astextplain *.jpg filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text *.bmp filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000000..d76d68c1a5 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,148 @@ +name: Build + +on: + push: + branches: + - master + tags: + - "v*" + pull_request: + branches: + - master + +jobs: + Build: + strategy: + matrix: + options: + - os: ubuntu-latest + framework: netcoreapp3.1 + runtime: -x64 + codecov: false + - os: windows-latest + framework: netcoreapp3.1 + runtime: -x64 + codecov: true + - os: windows-latest + framework: netcoreapp2.1 + runtime: -x64 + codecov: false + - os: windows-latest + framework: net472 + runtime: -x64 + codecov: false + - os: windows-latest + framework: net472 + runtime: -x86 + codecov: false + + runs-on: ${{matrix.options.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install NuGet + uses: NuGet/setup-nuget@v1 + + - name: Setup Git + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + git fetch --prune --unshallow + git submodule -q update --init --recursive + + - name: Fetch Tags for GitVersion + run: | + git fetch --tags + + - name: Fetch master for GitVersion + if: github.ref != 'refs/heads/master' + run: git branch --create-reflog master origin/master + + - name: Install GitVersion + uses: gittools/actions/setup-gitversion@v0.3 + with: + versionSpec: "5.1.x" + + - name: Use GitVersion + id: gitversion # step id used as reference for output values + uses: gittools/actions/execute-gitversion@v0.3 + + - name: Setup DotNet SDK + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "3.1.101" + + - name: Build + shell: pwsh + run: ./ci-build.ps1 "${{steps.gitversion.outputs.nuGetVersion}}" "${{matrix.options.framework}}" + + - name: Test + shell: pwsh + run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}" + env: + CI: True + XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit + + # Avoid "Please provide the repository token to upload reports via `-t :repository-token`" + # https://community.codecov.io/t/whitelist-github-action-servers-to-upload-without-a-token/491/10 + # https://github.community/t5/GitHub-Actions/Make-secrets-available-to-builds-of-forks/m-p/42814/highlight/true#M5129 + - name: Update Codecov + uses: iansu/codecov-action-node@v1.0.0 + if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') + with: + token: 0ef021c7-2679-4012-b42f-4bed33d99450 + flags: unittests + + Publish: + needs: [Build] + + runs-on: windows-latest + + if: (github.event_name == 'push') + + steps: + - uses: actions/checkout@v2 + + - name: Install NuGet + uses: NuGet/setup-nuget@v1 + + - name: Setup Git + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.longpaths true + git fetch --prune --unshallow + git submodule -q update --init --recursive + + - name: Fetch Tags for GitVersion + run: | + git fetch --tags + + - name: Fetch master for GitVersion + if: github.ref != 'refs/heads/master' + run: git branch --create-reflog master origin/master + + - name: Install GitVersion + uses: gittools/actions/setup-gitversion@v0.3 + with: + versionSpec: "5.1.x" + + - name: Use GitVersion + id: gitversion # step id used as reference for output values + uses: gittools/actions/execute-gitversion@v0.3 + + - name: Setup DotNet SDK + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "3.1.101" + + - name: Pack + shell: pwsh + run: ./ci-pack.ps1 "${{steps.gitversion.outputs.nuGetVersion}}" + + - name: Publish to MyGet + shell: pwsh + run: nuget.exe push .\artifacts\*.nupkg ${{secrets.MYGET_TOKEN}} -Source https://www.myget.org/F/sixlabors/api/v2/package + # TODO: If github.ref starts with 'refs/tags' then it was tag push and we can optionally push out package to nuget.org diff --git a/.gitignore b/.gitignore index d8f376a419..a89cfcf104 100644 --- a/.gitignore +++ b/.gitignore @@ -137,7 +137,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj @@ -216,7 +216,7 @@ artifacts/ *.csproj.bak #CodeCoverage -/ImageSharp.Coverage.xml +*.lcov # Tests **/Images/ActualOutput diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6fd38484dd..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,43 +0,0 @@ -language: csharp -solution: ImageSharp.sln - -matrix: - include: - - os: linux # Ubuntu 16.04 - dist: xenial - sudo: required - dotnet: 2.1.603 - mono: latest -# - os: osx # OSX 10.11 -# osx_image: xcode7.3.1 -# dotnet: 1.0.0-preview2-003121 -# mono: latest - -branches: - only: - - master - - coverity_scan - -script: - - git submodule -q update --init - - dotnet restore - - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp2.1" - -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: "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- - -addons: - coverity_scan: - project: - name: "SixLabors/ImageSharp" - description: "Build submitted via Travis CI" - notification_email: james_south@hotmail.com - build_command_prepend: "dotnet restore" - build_command: "dotnet build -c Release" - branch_pattern: coverity_scan \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index c772e647ce..0000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - // 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", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceRoot}/tests/ImageSharp.Benchmarks/bin/Debug/netcoreapp2.0/ImageSharp.Benchmarks.dll", - "args": [], - "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" - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach", - "processId": "${command:pickProcess}" - } - ] -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 82aaa2f8d0..0000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "0.1.0", - "command": "dotnet", - "isShellCommand": true, - "args": [], - "tasks": [ - { - "taskName": "build", - "args": [ "ImageSharp.sln" ], - "isBuildCommand": true, - "showOutput": "always", - "problemMatcher": "$msCompile" - }, - { - "taskName": "build benchmark", - "suppressTaskName": true, - "args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp2.0", "-c", "Release" ], - "showOutput": "always", - "problemMatcher": "$msCompile" - }, - { - "taskName": "test", - "args": ["tests/ImageSharp.Tests/ImageSharp.Tests.csproj", "-c", "release", "-f", "netcoreapp2.0"], - "isTestCommand": true, - "showOutput": "always", - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/CodeCoverage.runsettings b/CodeCoverage.runsettings deleted file mode 100644 index d9c0848f13..0000000000 --- a/CodeCoverage.runsettings +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - .*ImageSharp.dll - - - .*tests* - .*Tests* - - - - - - - - \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index efe4cc9665..604153f976 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,7 +14,7 @@ $(MSBuildThisFileDirectory)artifacts/ $(ImageSharpProjectCategory)/$(MSBuildProjectName) - https://github.com/SixLabors/ImageSharp/ + https://github.com/SixLabors/ImageSharp/ @@ -29,13 +29,38 @@ true - - true + + + + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE + + + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE + + + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_CODECOVERAGE + + + $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE + + + $(DefineConstants);SUPPORTS_CODECOVERAGE - - - - false + + $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_CODECOVERAGE @@ -56,26 +81,33 @@ Copyright © Six Labors and Contributors strict;IOperation true - 7.3 + 8.0 en true - https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png + icon.png Apache-2.0 $(RepositoryUrl) true git https://www.myget.org/F/sixlabors/api/v3/index.json; - https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; https://api.nuget.org/v3/index.json; + + https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json; 002400000c8000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97 true - + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 1551a29d8c..e5c44f7761 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -15,33 +15,23 @@ $(DefineConstants);$(OS) - - - - - - - - - - - - - - - - - + + + + + + + - + + + - - - + diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000000..f2a251c55a --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,7 @@ +continuous-delivery-fallback-tag: ci +branches: + master: + tag: unstable + mode: ContinuousDeployment + pull-request: + tag: pr diff --git a/ImageSharp.sln b/ImageSharp.sln index d4a0419eed..40878c5751 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -3,24 +3,20 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28902.138 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore .gitmodules = .gitmodules - .travis.yml = .travis.yml - appveyor.yml = appveyor.yml - build.cmd = build.cmd - build.ps1 = build.ps1 - codecov.yml = codecov.yml - CodeCoverage.runsettings = CodeCoverage.runsettings + ci-build.ps1 = ci-build.ps1 + ci-pack.ps1 = ci-pack.ps1 + ci-test.ps1 = ci-test.ps1 Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings + GitVersion.yml = GitVersion.yml LICENSE = LICENSE README.md = README.md - run-tests.ps1 = run-tests.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1799C43E-5C54-4A8F-8D64-B1475241DB0D}" @@ -36,25 +32,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEM .github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".vscode", ".vscode", "{0274D4CF-9932-47CC-8E89-54DC05B8F06E}" - ProjectSection(SolutionItems) = preProject - .vscode\launch.json = .vscode\launch.json - .vscode\tasks.json = .vscode\tasks.json - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{E919DF0B-2607-4462-8FC0-5C98FE50F8C9}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "icons", "icons", "{2B02E303-7CC6-4E15-97EE-DBE86B287553}" - ProjectSection(SolutionItems) = preProject - build\icons\imagesharp-logo-128.png = build\icons\imagesharp-logo-128.png - build\icons\imagesharp-logo-256.png = build\icons\imagesharp-logo-256.png - build\icons\imagesharp-logo-32.png = build\icons\imagesharp-logo-32.png - build\icons\imagesharp-logo-512.png = build\icons\imagesharp-logo-512.png - build\icons\imagesharp-logo-64.png = build\icons\imagesharp-logo-64.png - build\icons\imagesharp-logo.png = build\icons\imagesharp-logo.png - build\icons\imagesharp-logo.svg = build\icons\imagesharp-logo.svg - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" ProjectSection(SolutionItems) = preProject src\Directory.Build.props = src\Directory.Build.props @@ -64,20 +41,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3 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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" ProjectSection(SolutionItems) = preProject tests\Directory.Build.props = tests\Directory.Build.props tests\Directory.Build.targets = tests\Directory.Build.targets EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeCoverage", "CodeCoverage", "{D4C5EC58-F8E6-4636-B9EE-C99D2578E5C6}" - ProjectSection(SolutionItems) = preProject - tests\CodeCoverage\CodeCoverage.cmd = tests\CodeCoverage\CodeCoverage.cmd - tests\CodeCoverage\packages.config = tests\CodeCoverage\packages.config - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{FA55F5DE-11A6-487D-ABA4-BC93A02717DD}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Input", "Input", "{9DA226A1-8656-49A8-A58A-A8B5C081AD66}" @@ -354,9 +323,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\I 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", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C0D7754B-5277-438E-ABEB-2BA34401B5A7}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml + EndProjectSection +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.shproj", "{68A8CC40-6AED-4E96-B524-31B1158FDEEA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests.ProfilingSandbox", "tests\ImageSharp.Tests.ProfilingSandbox\ImageSharp.Tests.ProfilingSandbox.csproj", "{FC527290-2F22-432C-B77B-6E815726B02C}" EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 @@ -378,18 +357,6 @@ Global {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x64.Build.0 = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x86.ActiveCfg = Release|Any CPU {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|x86.Build.0 = Release|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|x64.ActiveCfg = Debug|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|x64.Build.0 = Debug|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|x86.ActiveCfg = Debug|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Debug|x86.Build.0 = Debug|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|Any CPU.Build.0 = Release|Any CPU - {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x64.ActiveCfg = Release|Any CPU - {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 {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 @@ -414,28 +381,25 @@ 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 - {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 + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x64.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Debug|x86.Build.0 = Debug|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.Build.0 = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.ActiveCfg = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x64.Build.0 = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x86.ActiveCfg = Release|Any CPU + {FC527290-2F22-432C-B77B-6E815726B02C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {FBE8C1AD-5AEC-4514-9B64-091D8E145865} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D} - {2B02E303-7CC6-4E15-97EE-DBE86B287553} = {E919DF0B-2607-4462-8FC0-5C98FE50F8C9} {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} - {2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} - {D4C5EC58-F8E6-4636-B9EE-C99D2578E5C6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {FA55F5DE-11A6-487D-ABA4-BC93A02717DD} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {9DA226A1-8656-49A8-A58A-A8B5C081AD66} = {FA55F5DE-11A6-487D-ABA4-BC93A02717DD} {1A82C5F6-90E0-4E97-BE16-A825C046B493} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} @@ -452,7 +416,9 @@ Global {E1C42A6F-913B-4A7B-B1A8-2BB62843B254} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {C0D7754B-5277-438E-ABEB-2BA34401B5A7} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D} + {68A8CC40-6AED-4E96-B524-31B1158FDEEA} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {FC527290-2F22-432C-B77B-6E815726B02C} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings deleted file mode 100644 index ece3dddb3c..0000000000 --- a/ImageSharp.sln.DotSettings +++ /dev/null @@ -1,393 +0,0 @@ - - <?xml version="1.0" encoding="utf-16"?> -<Profile name="StyleCop"> - <CSUpdateFileHeader>False</CSUpdateFileHeader> - <CSArrangeQualifiers>True</CSArrangeQualifiers> - <CSOptimizeUsings> - <OptimizeUsings>True</OptimizeUsings> - <EmbraceInRegion>False</EmbraceInRegion> - <RegionName></RegionName> - </CSOptimizeUsings> - <CSReformatCode>True</CSReformatCode> - <CSReorderTypeMembers>True</CSReorderTypeMembers> -</Profile> - StyleCop - public protected internal private static new abstract virtual override sealed readonly extern unsafe volatile async - Field, Property, Event, Method - True - True - True - True - True - True - True - True - True - NEXT_LINE_SHIFTED_2 - 1 - 1 - 1 - 1 - 1 - NEXT_LINE_SHIFTED_2 - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - NEXT_LINE_SHIFTED_2 - 1 - 1 - False - False - False - NEVER - False - False - NEVER - False - ALWAYS - False - True - ON_SINGLE_LINE - False - True - True - False - True - True - CHOP_IF_LONG - True - True - CHOP_IF_LONG - CHOP_IF_LONG - <?xml version="1.0" encoding="utf-16"?> -<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> - <TypePattern DisplayName="COM interfaces or structs"> - <TypePattern.Match> - <Or> - <And> - <Kind Is="Interface" /> - <Or> - <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /> - <HasAttribute Name="System.Runtime.InteropServices.ComImport" /> - </Or> - </And> - <Kind Is="Struct" /> - </Or> - </TypePattern.Match> - </TypePattern> - <TypePattern DisplayName="P/Invoke classes called 'NativeMethods' (StyleCop)"> - <TypePattern.Match> - <And> - <Kind Is="Class" /> - <Name Is=".*NativeMethods" /> - </And> - </TypePattern.Match> - </TypePattern> - <TypePattern DisplayName="DataMember serialisation classes (StyleCop)"> - <TypePattern.Match> - <And> - <Or> - <Kind Is="Field" /> - <Kind Is="Property" /> - </Or> - <HasAttribute Name="System.Runtime.Serialization.DataMemberAttribute" /> - </And> - </TypePattern.Match> - </TypePattern> - <TypePattern DisplayName="Default Pattern (StyleCop)" RemoveRegions="All"> - <Entry DisplayName="Constants"> - <Entry.Match> - <Kind Is="Constant" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Static fields"> - <Entry.Match> - <And> - <Kind Is="Field" /> - <Static /> - </And> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Readonly /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Fields"> - <Entry.Match> - <Kind Is="Field" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Readonly /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="200" DisplayName="Constructors and Destructors"> - <Entry.Match> - <Or> - <Kind Is="Constructor" /> - <Kind Is="Destructor" /> - </Or> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Kind Order="Constructor Destructor" /> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Delegates"> - <Entry.Match> - <Kind Is="Delegate" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public events"> - <Entry.Match> - <And> - <Kind Is="Event" /> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface events"> - <Entry.Match> - <And> - <Kind Is="Event" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other events"> - <Entry.Match> - <Kind Is="Event" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Enums"> - <Entry.Match> - <Kind Is="Enum" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interfaces"> - <Entry.Match> - <Kind Is="Interface" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public properties"> - <Entry.Match> - <And> - <Kind Is="Property" /> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface properties"> - <Entry.Match> - <And> - <Kind Is="Property" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other properties"> - <Entry.Match> - <Kind Is="Property" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="1000" DisplayName="Public indexers"> - <Entry.Match> - <And> - <Kind Is="Indexer" /> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="1000" DisplayName="Interface indexers"> - <Entry.Match> - <And> - <Kind Is="Indexer" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="1000" DisplayName="Other indexers"> - <Entry.Match> - <Kind Is="Indexer" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Public methods and operators"> - <Entry.Match> - <And> - <Or> - <Kind Is="Method" /> - <Kind Is="Operator" /> - </Or> - <Access Is="Public" /> - </And> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Interface methods"> - <Entry.Match> - <And> - <Kind Is="Method" /> - <ImplementsInterface /> - </And> - </Entry.Match> - <Entry.SortBy> - <ImplementsInterface Immediate="True" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Other methods"> - <Entry.Match> - <Kind Is="Method" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="Operators"> - <Entry.Match> - <Kind Is="Operator" /> - </Entry.Match> - <Entry.SortBy> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Static /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="600" DisplayName="Nested structs"> - <Entry.Match> - <Kind Is="Struct" /> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry Priority="700" DisplayName="Nested classes"> - <Entry.Match> - <Kind Is="Class" /> - </Entry.Match> - <Entry.SortBy> - <Static /> - <Access Order="Public Internal ProtectedInternal Protected Private" /> - <Name /> - </Entry.SortBy> - </Entry> - <Entry DisplayName="All other members" /> - </TypePattern> -</Patterns> - False - True - // Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - - AC - DC - DCT - EOF - FDCT - IDCT - JPEG - MCU - PNG - RGB - RLE - XY - XYZ - $object$_On$event$ - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <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 - True - True - True - \ No newline at end of file diff --git a/README.md b/README.md index 28c3770373..af8d4f73ae 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ SixLabors.ImageSharp
+[![Build Status](https://img.shields.io/github/workflow/status/SixLabors/ImageSharp/Build/master)](https://github.com/SixLabors/ImageSharp/actions) [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/SixLabors/ImageSharp/master/LICENSE) [![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/http/shields.io.svg?style=flat&logo=twitter)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors) @@ -35,7 +36,6 @@ Install stable releases via Nuget; development releases are available via MyGet. | 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 @@ -46,18 +46,13 @@ The **ImageSharp** library is made up of multiple packages: - 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 -- **SixLabors.ImageSharp.Drawing** - - Brushes and various drawing algorithms, including drawing images - - Various vector drawing methods for drawing paths, polygons etc. - - Text drawing - + ### 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. **Do not** open issues for questions! @@ -80,7 +75,7 @@ using SixLabors.ImageSharp.PixelFormats; // 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")) +using (Image image = Image.Load("foo.jpg")) { image.Mutate(x => x .Resize(image.Width / 2, image.Height / 2) @@ -113,9 +108,9 @@ For more examples check out: If you prefer, you can compile ImageSharp yourself (please do and help!) -- Using [Visual Studio 2017](https://visualstudio.microsoft.com/vs/) +- Using [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) - Make sure you have the latest version installed - - Make sure you have [the .NET Core 2.1 SDK](https://www.microsoft.com/net/core#windows) installed + - Make sure you have [the .NET Core 3.1 SDK](https://www.microsoft.com/net/core#windows) installed Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**: @@ -155,6 +150,7 @@ Core Team - [Dirk Lemstra](https://github.com/dlemstra) - [Anton Firsov](https://github.com/antonfirsov) - [Scott Williams](https://github.com/tocsoft) +- [Brian Popow](https://github.com/brianpopow) ### Backers diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 2cc5182d39..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,69 +0,0 @@ -version: 1.0.0.{build} -image: Visual Studio 2017 - -# prevent the double build when a branch has an active PR -skip_branch_with_pr: true - -environment: - matrix: - - target_framework: netcoreapp2.1 - is_32bit: False - - - target_framework: netcoreapp2.1 - is_32bit: True - - - target_framework: net472 - is_32bit: False - - - target_framework: net472 - is_32bit: True - - - target_framework: net462 - is_32bit: False - - - target_framework: net462 - is_32bit: True - - #- target_framework: mono - # is_32bit: False - #- target_framework: mono - # is_32bit: True - #- target_framework: net47 - # is_32bit: False - #- target_framework: net47 - # is_32bit: True - -install: - - ps: | - if ($env:target_framework -eq "mono") { - if ($env:is_32bit -eq "True") { - cinst mono --x86 - } else { - cinst mono - } - } - -before_build: - - git submodule -q update --init - - cmd: dotnet --info - -build_script: -- cmd: build.cmd - -test_script: -- ps: .\run-tests.ps1 $env:target_framework $env:is_32bit - -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/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 \ No newline at end of file diff --git a/build.cmd b/build.cmd deleted file mode 100644 index 6372b41253..0000000000 --- a/build.cmd +++ /dev/null @@ -1,17 +0,0 @@ -@echo Off - -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 deleted file mode 100644 index 215b551170..0000000000 --- a/build.ps1 +++ /dev/null @@ -1,122 +0,0 @@ - -# 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 /p:DisableImplicitNuGetFallbackFolder=true - -Write-Host "Building projects" -dotnet build -c Release /p:packageversion=$version - -if ($LASTEXITCODE ){ Exit $LASTEXITCODE } - -# -# TODO: DO WE NEED TO RUN TESTS IMPLICITLY? -# -# if ( $env:CI -ne "True") { -# cd ./tests/ImageSharp.Tests/ -# dotnet xunit -nobuild -c Release -f netcoreapp2.0 --fx-version 2.0.0 -# ./RunExtendedTests.cmd -# cd ../.. -# } -# - -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/icons/imagesharp-logo-128.png b/build/icons/imagesharp-logo-128.png deleted file mode 100644 index 5c2079144f..0000000000 --- a/build/icons/imagesharp-logo-128.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:148a268c589b628f5d0b5af0e86911a0b393c35b8b25233c71553657c88e0b96 -size 7568 diff --git a/build/icons/imagesharp-logo-256.png b/build/icons/imagesharp-logo-256.png deleted file mode 100644 index e38807ae1e..0000000000 --- a/build/icons/imagesharp-logo-256.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7e4b2ff72aef1979500cd130c28490a00be116bb833bc96ca30c85dc0596099c -size 15413 diff --git a/build/icons/imagesharp-logo-32.png b/build/icons/imagesharp-logo-32.png deleted file mode 100644 index 273b171eb2..0000000000 --- a/build/icons/imagesharp-logo-32.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:021c12313afbdc65f58bfea8c7b436d5c2102513bb63d9e64ee2b61a1344c56a -size 1799 diff --git a/build/icons/imagesharp-logo-512.png b/build/icons/imagesharp-logo-512.png deleted file mode 100644 index 707dc9a35b..0000000000 --- a/build/icons/imagesharp-logo-512.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ae54ae0035df1f8f1459081e2f1d5cceda6f88cca6ec015d8c0209bf0d34edf -size 32534 diff --git a/build/icons/imagesharp-logo-64.png b/build/icons/imagesharp-logo-64.png deleted file mode 100644 index 17577772eb..0000000000 --- a/build/icons/imagesharp-logo-64.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:92896854265693f28f9a503b9093cb2c9a4a9b329f310732efdd9c6f6c3761bc -size 3736 diff --git a/build/icons/imagesharp-logo.png b/build/icons/imagesharp-logo.png deleted file mode 100644 index 707dc9a35b..0000000000 --- a/build/icons/imagesharp-logo.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ae54ae0035df1f8f1459081e2f1d5cceda6f88cca6ec015d8c0209bf0d34edf -size 32534 diff --git a/build/icons/imagesharp-logo.svg b/build/icons/imagesharp-logo.svg deleted file mode 100644 index 620287457a..0000000000 --- a/build/icons/imagesharp-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ci-build.ps1 b/ci-build.ps1 new file mode 100644 index 0000000000..17c6e6603b --- /dev/null +++ b/ci-build.ps1 @@ -0,0 +1,13 @@ +param( + [Parameter(Mandatory, Position = 0)] + [string]$version, + [Parameter(Mandatory = $true, Position = 1)] + [string]$targetFramework +) + +dotnet clean -c Release + +$repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" + +# Building for a specific framework. +dotnet build -c Release -f $targetFramework /p:packageversion=$version /p:RepositoryUrl=$repositoryUrl diff --git a/ci-pack.ps1 b/ci-pack.ps1 new file mode 100644 index 0000000000..a4e846db95 --- /dev/null +++ b/ci-pack.ps1 @@ -0,0 +1,11 @@ +param( + [Parameter(Mandatory, Position = 0)] + [string]$version +) + +dotnet clean -c Release + +$repositoryUrl = "https://github.com/$env:GITHUB_REPOSITORY" + +# Building for packing and publishing. +dotnet pack -c Release --output "$PSScriptRoot/artifacts" /p:packageversion=$version /p:RepositoryUrl=$repositoryUrl diff --git a/ci-test.ps1 b/ci-test.ps1 new file mode 100644 index 0000000000..3915ae4ccd --- /dev/null +++ b/ci-test.ps1 @@ -0,0 +1,37 @@ +param( + [Parameter(Mandatory, Position = 0)] + [string]$os, + [Parameter(Mandatory, Position = 1)] + [string]$targetFramework, + [Parameter(Mandatory, Position = 2)] + [string]$platform, + [Parameter(Mandatory, Position = 3)] + [string]$codecov, + [Parameter(Position = 4)] + [string]$codecovProfile = 'Release' +) + +$netFxRegex = '^net\d+' + +if ($codecov -eq 'true') { + + # Allow toggling of profile to workaround any potential JIT errors caused by code injection. + dotnet clean -c $codecovProfile + dotnet test --collect "XPlat Code Coverage" --settings .\tests\coverlet.runsettings -c $codecovProfile -f $targetFramework /p:CodeCov=true +} +elseif ($platform -eq '-x86' -and $targetFramework -match $netFxRegex) { + + # xunit doesn't run on core with NET SDK 3.1+. + # xunit doesn't actually understand -x64 as an option. + # + # xunit requires explicit path. + Set-Location $env:XUNIT_PATH + + dotnet xunit --no-build -c Release -f $targetFramework ${fxVersion} $platform + + Set-Location $PSScriptRoot +} +else { + + dotnet test --no-build -c Release -f $targetFramework +} diff --git a/codecov.yml b/codecov.yml index ae6dd5f6bf..3941f7ff9a 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,4 +1,7 @@ -ignore: - "src/ImageSharp/Common/Helpers/DebugGuard.cs" +# Documentation: https://docs.codecov.io/docs/codecov-yaml - \ No newline at end of file +codecov: + # Avoid "Missing base report" + # https://github.com/codecov/support/issues/363 + # https://docs.codecov.io/docs/comparing-commits + allow_coverage_offsets: true diff --git a/run-tests.ps1 b/run-tests.ps1 deleted file mode 100644 index 4aeaa14908..0000000000 --- a/run-tests.ps1 +++ /dev/null @@ -1,112 +0,0 @@ -param( - [string]$targetFramework, - [string]$is32Bit = "False" -) - -if (!$targetFramework){ - Write-Host "run-tests.ps1 ERROR: targetFramework is undefined!" - exit 1 -} - -function VerifyPath($path, $errorMessage) { - if (!(Test-Path -Path $path)) { - Write-Host "run-tests.ps1 $errorMessage `n $xunitRunnerPath" - exit 1 - } -} - -function CheckSubmoduleStatus() { - $submoduleStatus = (git submodule status) | Out-String - # if the result string is empty, the command failed to run (we didn't capture the error stream) - if ($submoduleStatus) { - # git has been called successfully, what about the status? - if (($submoduleStatus -match "\-") -or ($submoduleStatus -match "\(\(null\)\)")) - { - # submodule has not been initialized! - return 2; - } - elseif ($submoduleStatus -match "\+") - { - # submodule is not synced: - return 1; - } - else { - # everything fine: - return 0; - } - } else { - # git call failed, so we should warn - return 3; - } -} - - -if ( ($targetFramework -eq "netcoreapp2.1") -and ($env:CI -eq "True") -and ($is32Bit -ne "True")) { - # We execute CodeCoverage.cmd only for one specific job on CI (netcoreapp2.1 + 64bit ) - $testRunnerCmd = ".\tests\CodeCoverage\CodeCoverage.cmd" -} -elseif ($targetFramework -eq "mono") { - $testDllPath = "$PSScriptRoot\tests\ImageSharp.Tests\bin\Release\net462\SixLabors.ImageSharp.Tests.dll" - VerifyPath($testDllPath, "test dll missing:") - - $xunitRunnerPath = "${env:HOMEPATH}\.nuget\packages\xunit.runner.console\2.3.1\tools\net452\" - - VerifyPath($xunitRunnerPath, "xunit console runner is missing on path:") - - cd "$xunitRunnerPath" - - if ($is32Bit -ne "True") { - $monoPath = "${env:PROGRAMFILES}\Mono\bin\mono.exe" - } - else { - $monoPath = "${env:ProgramFiles(x86)}\Mono\bin\mono.exe" - } - - VerifyPath($monoPath, "mono runtime missing:") - - $testRunnerCmd = "& `"${monoPath}`" .\xunit.console.exe `"${testDllPath}`"" -} -else { - cd .\tests\ImageSharp.Tests - $xunitArgs = "-nobuild -c Release -framework $targetFramework" - - if ($targetFramework -eq "netcoreapp2.1") { - # There were issues matching the correct installed runtime if we do not specify it explicitly: - $xunitArgs += " --fx-version 2.1.0" - } - - if ($is32Bit -eq "True") { - $xunitArgs += " -x86" - } - - $testRunnerCmd = "dotnet xunit $xunitArgs" -} - -Write-Host "running:" -Write-Host $testRunnerCmd -Write-Host "..." - -Invoke-Expression $testRunnerCmd - -cd $PSScriptRoot - -$exitCodeOfTests = $LASTEXITCODE; - -if (0 -ne ([int]$exitCodeOfTests)) { - # check submodule status - $submoduleStatus = CheckSubmoduleStatus - if ([int]$submoduleStatus -eq 1) { - # not synced - Write-Host -ForegroundColor Yellow "Check if submodules are up to date. You can use 'git submodule update' to fix this"; - } elseif ($submoduleStatus -eq 2) { - # not initialized - Write-Host -ForegroundColor Yellow "Check if submodules are initialized. You can run 'git submodule init' to initialize them." - } elseif ($submoduleStatus -eq 3) { - # git not found, maybe submodules not synced? - Write-Host -ForegroundColor Yellow "Could not check if submodules are initialized correctly. Maybe git is not installed?" - } else { - #Write-Host "Submodules are up to date"; - } -} - -exit $exitCodeOfTests diff --git a/shared-infrastructure b/shared-infrastructure index faf84e44ec..36b2d55f5b 160000 --- a/shared-infrastructure +++ b/shared-infrastructure @@ -1 +1 @@ -Subproject commit faf84e44ec90e8a42a7271bcd04fea76279efb08 +Subproject commit 36b2d55f5bb0d91024955bd26ba220ee41cc96e5 diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 6fbbb7c916..5e3f9b0618 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -22,11 +22,14 @@ true - - - + + true + + + + @@ -34,8 +37,4 @@ - - - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index c0e01ae586..68d4f8949c 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -50,6 +50,6 @@ - + diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj deleted file mode 100644 index 5a53d3e78b..0000000000 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - - SixLabors.ImageSharp.Drawing - SixLabors.ImageSharp.Drawing - An extension to ImageSharp that allows the drawing of images, paths, and text. - SixLabors.ImageSharp.Drawing - Image Draw Shape Path Font - SixLabors.ImageSharp - netcoreapp2.1;netstandard1.3;netstandard2.0 - - - - - $(DefineConstants);SUPPORTS_MATHF - - - - $(DefineConstants);SUPPORTS_HASHCODE - - - - - - - - - - - - diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings deleted file mode 100644 index a728b54979..0000000000 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Primitives/Region.cs b/src/ImageSharp.Drawing/Primitives/Region.cs deleted file mode 100644 index 27f039f122..0000000000 --- a/src/ImageSharp.Drawing/Primitives/Region.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Primitives -{ - /// - /// Represents a region of an image. - /// - public abstract class Region - { - /// - /// Gets the maximum number of intersections to could be returned. - /// - public abstract int MaxIntersections { get; } - - /// - /// Gets the bounding box that entirely surrounds this region. - /// - /// - /// This should always contains all possible points returned from . - /// - public abstract Rectangle Bounds { get; } - - /// - /// Scans the X axis for intersections at the Y axis position. - /// - /// The position along the y axis to find intersections. - /// The buffer. - /// A instance in the context of the caller. - /// The number of intersections found. - public abstract int Scan(float y, Span buffer, Configuration configuration); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Primitives/ShapePath.cs b/src/ImageSharp.Drawing/Primitives/ShapePath.cs deleted file mode 100644 index a4fef66a67..0000000000 --- a/src/ImageSharp.Drawing/Primitives/ShapePath.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Processing; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Primitives -{ - /// - /// A mapping between a and a region. - /// - internal class ShapePath : ShapeRegion - { - /// - /// Initializes a new instance of the class. - /// - /// The shape. - /// The pen to apply to the shape. - public ShapePath(IPath shape, IPen pen) - : base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern)) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs deleted file mode 100644 index f4a6458206..0000000000 --- a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Primitives -{ - /// - /// A mapping between a and a region. - /// - internal class ShapeRegion : Region - { - /// - /// Initializes a new instance of the class. - /// - /// The shape. - public ShapeRegion(IPath shape) - { - this.Shape = shape.AsClosedPath(); - int left = (int)MathF.Floor(shape.Bounds.Left); - int top = (int)MathF.Floor(shape.Bounds.Top); - - int right = (int)MathF.Ceiling(shape.Bounds.Right); - int bottom = (int)MathF.Ceiling(shape.Bounds.Bottom); - this.Bounds = Rectangle.FromLTRB(left, top, right, bottom); - } - - /// - /// Gets the fillable shape - /// - public IPath Shape { get; } - - /// - public override int MaxIntersections => this.Shape.MaxIntersections; - - /// - public override Rectangle Bounds { get; } - - /// - public override int Scan(float y, Span buffer, Configuration configuration) - { - var start = new PointF(this.Bounds.Left - 1, y); - var end = new PointF(this.Bounds.Right + 1, y); - - using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(buffer.Length)) - { - Span innerBuffer = tempBuffer.GetSpan(); - int count = this.Shape.FindIntersections(start, end, innerBuffer); - - for (int i = 0; i < count; i++) - { - buffer[i] = innerBuffer[i].X; - } - - return count; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs deleted file mode 100644 index 7e75d7effc..0000000000 --- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// primitive that converts a point in to a color for discovering the fill color based on an implementation - /// - /// The pixel format. - /// - public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The target. - /// The options. - internal BrushApplicator(ImageFrame target, GraphicsOptions options) - { - this.Target = target; - this.Options = options; - this.Blender = PixelOperations.Instance.GetPixelBlender(options); - } - - /// - /// Gets the blender - /// - internal PixelBlender Blender { get; } - - /// - /// Gets the destination - /// - protected ImageFrame Target { get; } - - /// - /// Gets the blend percentage - /// - protected GraphicsOptions Options { get; } - - /// - /// Gets the color for a single pixel. - /// - /// The x coordinate. - /// The y coordinate. - /// The a that should be applied to the pixel. - internal abstract TPixel this[int x, int y] { get; } - - /// - public abstract void Dispose(); - - /// - /// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush. - /// - /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. - /// The x position in the target pixel space that the start of the scanline data corresponds to. - /// The y position in the target pixel space that whole scanline corresponds to. - /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. - internal virtual void Apply(Span scanline, int x, int y) - { - MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - - using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) - using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) - { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); - - for (int i = 0; i < scanline.Length; i++) - { - if (this.Options.BlendPercentage < 1) - { - amountSpan[i] = scanline[i] * this.Options.BlendPercentage; - } - else - { - amountSpan[i] = scanline[i]; - } - - overlaySpan[i] = this[x + i, y]; - } - - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); - } - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/Brushes.cs b/src/ImageSharp.Drawing/Processing/Brushes.cs deleted file mode 100644 index bd10e90c68..0000000000 --- a/src/ImageSharp.Drawing/Processing/Brushes.cs +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// A collection of methods for creating generic brushes. - /// - /// A New - public static class Brushes - { - /// - /// Percent10 Hatch Pattern - /// - /// ---> x axis - /// ^ - /// | y - axis - /// | - /// see PatternBrush for details about how to make new patterns work - private static readonly bool[,] Percent10Pattern = - { - { true, false, false, false }, - { false, false, false, false }, - { false, false, true, false }, - { false, false, false, false } - }; - - /// - /// Percent20 pattern. - /// - private static readonly bool[,] Percent20Pattern = - { - { true, false, false, false }, - { false, false, true, false }, - { true, false, false, false }, - { false, false, true, false } - }; - - /// - /// Horizontal Hatch Pattern - /// - private static readonly bool[,] HorizontalPattern = - { - { false }, - { true }, - { false }, - { false } - }; - - /// - /// Min Pattern - /// - private static readonly bool[,] MinPattern = - { - { false }, - { false }, - { false }, - { true } - }; - - /// - /// Vertical Pattern - /// - private static readonly bool[,] VerticalPattern = - { - { false, true, false, false }, - }; - - /// - /// Forward Diagonal Pattern - /// - private static readonly bool[,] ForwardDiagonalPattern = - { - { false, false, false, true }, - { false, false, true, false }, - { false, true, false, false }, - { true, false, false, false } - }; - - /// - /// Backward Diagonal Pattern - /// - private static readonly bool[,] BackwardDiagonalPattern = - { - { true, false, false, false }, - { false, true, false, false }, - { false, false, true, false }, - { false, false, false, true } - }; - - /// - /// Create as brush that will paint a solid color - /// - /// The color. - /// A New - public static SolidBrush Solid(Color color) => new SolidBrush(color); - - /// - /// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// A New - public static PatternBrush Percent10(Color foreColor) => - new PatternBrush(foreColor, Color.Transparent, Percent10Pattern); - - /// - /// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush Percent10(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, Percent10Pattern); - - /// - /// Create as brush that will paint a Percent20 Hatch Pattern with the specified foreground color and a - /// transparent background. - /// - /// Color of the foreground. - /// A New - public static PatternBrush Percent20(Color foreColor) => - new PatternBrush(foreColor, Color.Transparent, Percent20Pattern); - - /// - /// Create as brush that will paint a Percent20 Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush Percent20(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, Percent20Pattern); - - /// - /// Create as brush that will paint a Horizontal Hatch Pattern with the specified foreground color and a - /// transparent background. - /// - /// Color of the foreground. - /// A New - public static PatternBrush Horizontal(Color foreColor) => - new PatternBrush(foreColor, Color.Transparent, HorizontalPattern); - - /// - /// Create as brush that will paint a Horizontal Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush Horizontal(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, HorizontalPattern); - - /// - /// Create as brush that will paint a Min Hatch Pattern with the specified foreground color and a - /// transparent background. - /// - /// Color of the foreground. - /// A New - public static PatternBrush Min(Color foreColor) => new PatternBrush(foreColor, Color.Transparent, MinPattern); - - /// - /// Create as brush that will paint a Min Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush Min(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, MinPattern); - - /// - /// Create as brush that will paint a Vertical Hatch Pattern with the specified foreground color and a - /// transparent background. - /// - /// Color of the foreground. - /// A New - public static PatternBrush Vertical(Color foreColor) => - new PatternBrush(foreColor, Color.Transparent, VerticalPattern); - - /// - /// Create as brush that will paint a Vertical Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush Vertical(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, VerticalPattern); - - /// - /// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified foreground color and a - /// transparent background. - /// - /// Color of the foreground. - /// A New - public static PatternBrush ForwardDiagonal(Color foreColor) => - new PatternBrush(foreColor, Color.Transparent, ForwardDiagonalPattern); - - /// - /// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush ForwardDiagonal(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, ForwardDiagonalPattern); - - /// - /// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified foreground color and a - /// transparent background. - /// - /// Color of the foreground. - /// A New - public static PatternBrush BackwardDiagonal(Color foreColor) => - new PatternBrush(foreColor, Color.Transparent, BackwardDiagonalPattern); - - /// - /// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A New - public static PatternBrush BackwardDiagonal(Color foreColor, Color backColor) => - new PatternBrush(foreColor, backColor, BackwardDiagonalPattern); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/ColorStop.cs b/src/ImageSharp.Drawing/Processing/ColorStop.cs deleted file mode 100644 index 21c82b63ff..0000000000 --- a/src/ImageSharp.Drawing/Processing/ColorStop.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Diagnostics; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// A struct that defines a single color stop. - /// - [DebuggerDisplay("ColorStop({Ratio} -> {Color}")] - public readonly struct ColorStop - { - /// - /// Initializes a new instance of the struct. - /// - /// Where should it be? 0 is at the start, 1 at the end of the Gradient. - /// What color should be used at that point? - public ColorStop(float ratio, in Color color) - { - this.Ratio = ratio; - this.Color = color; - } - - /// - /// Gets the point along the defined gradient axis. - /// - public float Ratio { get; } - - /// - /// Gets the color to be used. - /// - public Color Color { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs b/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs deleted file mode 100644 index 25a8204f2a..0000000000 --- a/src/ImageSharp.Drawing/Processing/DrawingHelpers.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - internal static class DrawingHelpers - { - /// - /// Convert a to a of the given pixel type. - /// - public static DenseMatrix ToPixelMatrix(this DenseMatrix colorMatrix, Configuration configuration) - where TPixel : struct, IPixel - { - var result = new DenseMatrix(colorMatrix.Columns, colorMatrix.Rows); - Color.ToPixel(configuration, colorMatrix.Span, result.Span); - return result; - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs deleted file mode 100644 index 91da332a16..0000000000 --- a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs +++ /dev/null @@ -1,168 +0,0 @@ -// 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 -{ - /// - /// Gradient Brush with elliptic shape. - /// The ellipse is defined by a center point, - /// a point on the longest extension of the ellipse and - /// the ratio between longest and shortest extension. - /// - public sealed class EllipticGradientBrush : GradientBrush - { - private readonly PointF center; - - private readonly PointF referenceAxisEnd; - - private readonly float axisRatio; - - /// - /// The center of the elliptical gradient and 0 for the color stops. - /// The end point of the reference axis of the ellipse. - /// - /// The ratio of the axis widths. - /// The second axis' is perpendicular to the reference axis and - /// it's length is the reference axis' length multiplied by this factor. - /// - /// Defines how the colors of the gradients are repeated. - /// the color stops as defined in base class. - public EllipticGradientBrush( - PointF center, - PointF referenceAxisEnd, - float axisRatio, - GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) - : base(repetitionMode, colorStops) - { - this.center = center; - this.referenceAxisEnd = referenceAxisEnd; - this.axisRatio = axisRatio; - } - - /// - public override BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) => - new RadialGradientBrushApplicator( - source, - options, - this.center, - this.referenceAxisEnd, - this.axisRatio, - this.ColorStops, - this.RepetitionMode); - - /// - private sealed class RadialGradientBrushApplicator : GradientBrushApplicator - where TPixel : struct, IPixel - { - private readonly PointF center; - - private readonly PointF referenceAxisEnd; - - private readonly float axisRatio; - - private readonly double rotation; - - private readonly float referenceRadius; - - private readonly float secondRadius; - - private readonly float cosRotation; - - private readonly float sinRotation; - - private readonly float secondRadiusSquared; - - private readonly float referenceRadiusSquared; - - /// - /// Initializes a new instance of the class. - /// - /// The target image - /// The options - /// Center of the ellipse - /// Point on one angular points of the ellipse. - /// - /// Ratio of the axis length's. Used to determine the length of the second axis, - /// the first is defined by and . - /// Definition of colors - /// Defines how the gradient colors are repeated. - public RadialGradientBrushApplicator( - ImageFrame target, - GraphicsOptions options, - PointF center, - PointF referenceAxisEnd, - float axisRatio, - ColorStop[] colorStops, - GradientRepetitionMode repetitionMode) - : base(target, options, colorStops, repetitionMode) - { - this.center = center; - this.referenceAxisEnd = referenceAxisEnd; - this.axisRatio = axisRatio; - this.rotation = this.AngleBetween( - this.center, - new PointF(this.center.X + 1, this.center.Y), - this.referenceAxisEnd); - this.referenceRadius = this.DistanceBetween(this.center, this.referenceAxisEnd); - this.secondRadius = this.referenceRadius * this.axisRatio; - - this.referenceRadiusSquared = this.referenceRadius * this.referenceRadius; - this.secondRadiusSquared = this.secondRadius * this.secondRadius; - - this.sinRotation = (float)Math.Sin(this.rotation); - this.cosRotation = (float)Math.Cos(this.rotation); - } - - /// - public override void Dispose() - { - } - - /// - protected override float PositionOnGradient(float xt, float yt) - { - float x0 = xt - this.center.X; - float y0 = yt - this.center.Y; - - float x = (x0 * this.cosRotation) - (y0 * this.sinRotation); - float y = (x0 * this.sinRotation) + (y0 * this.cosRotation); - - float xSquared = x * x; - float ySquared = y * y; - - var inBoundaryChecker = (xSquared / this.referenceRadiusSquared) - + (ySquared / this.secondRadiusSquared); - - return inBoundaryChecker; - } - - private float AngleBetween(PointF junction, PointF a, PointF b) - { - var vA = a - junction; - var vB = b - junction; - return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X); - } - - private float DistanceBetween( - PointF p1, - PointF p2) - { - float dX = p1.X - p2.X; - float dXsquared = dX * dX; - - float dY = p1.Y - p2.Y; - float dYsquared = dY * dY; - return MathF.Sqrt(dXsquared + dYsquared); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs deleted file mode 100644 index 7660e72255..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of Bezier paths to the type. - /// - public static class DrawBezierExtensions - { - /// - /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawBeziers( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - float thickness, - params PointF[] points) => - source.Draw(options, new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); - - /// - /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The brush. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawBeziers( - this IImageProcessingContext source, - IBrush brush, - float thickness, - params PointF[] points) => - source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); - - /// - /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The color. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawBeziers( - this IImageProcessingContext source, - Color color, - float thickness, - params PointF[] points) => - source.DrawBeziers(new SolidBrush(color), thickness, points); - - /// - /// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The options. - /// The color. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawBeziers( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - float thickness, - params PointF[] points) => - source.DrawBeziers(options, new SolidBrush(color), thickness, points); - - /// - /// Draws the provided points as an open Bezier path with the supplied pen - /// - /// The image this method extends. - /// The options. - /// The pen. - /// The points. - /// The . - public static IImageProcessingContext DrawBeziers( - this IImageProcessingContext source, - GraphicsOptions options, - IPen pen, - params PointF[] points) => - source.Draw(options, pen, new Path(new CubicBezierLineSegment(points))); - - /// - /// Draws the provided points as an open Bezier path with the supplied pen - /// - /// The image this method extends. - /// The pen. - /// The points. - /// The . - public static IImageProcessingContext DrawBeziers( - this IImageProcessingContext source, - IPen pen, - params PointF[] points) => - source.Draw(pen, new Path(new CubicBezierLineSegment(points))); - } -} diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs deleted file mode 100644 index 98e8fdc594..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of lines to the type. - /// - public static class DrawLineExtensions - { - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawLines( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - float thickness, - params PointF[] points) => - source.Draw(options, new Pen(brush, thickness), new Path(new LinearLineSegment(points))); - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The brush. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawLines( - this IImageProcessingContext source, - IBrush brush, - float thickness, - params PointF[] points) => - source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The color. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawLines( - this IImageProcessingContext source, - Color color, - float thickness, - params PointF[] points) => - source.DrawLines(new SolidBrush(color), thickness, points); - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The image this method extends. - /// The options. - /// The color. - /// The thickness. - /// The points. - /// The .> - public static IImageProcessingContext DrawLines( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - float thickness, - params PointF[] points) => - source.DrawLines(options, new SolidBrush(color), thickness, points); - - /// - /// Draws the provided Points as an open Linear path with the supplied pen - /// - /// The image this method extends. - /// The options. - /// The pen. - /// The points. - /// The . - public static IImageProcessingContext DrawLines( - this IImageProcessingContext source, - GraphicsOptions options, - IPen pen, - params PointF[] points) => - source.Draw(options, pen, new Path(new LinearLineSegment(points))); - - /// - /// Draws the provided Points as an open Linear path with the supplied pen - /// - /// The image this method extends. - /// The pen. - /// The points. - /// The . - public static IImageProcessingContext DrawLines( - this IImageProcessingContext source, - IPen pen, - params PointF[] points) => - source.Draw(pen, new Path(new LinearLineSegment(points))); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs deleted file mode 100644 index a68b69a444..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of collections of polygon outlines to the type. - /// - public static class DrawPathCollectionExtensions - { - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The image this method extends. - /// The options. - /// The pen. - /// The paths. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - IPen pen, - IPathCollection paths) - { - foreach (IPath path in paths) - { - source.Draw(options, pen, path); - } - - return source; - } - - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The image this method extends. - /// The pen. - /// The paths. - /// The . - public static IImageProcessingContext - Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) => - source.Draw(GraphicsOptions.Default, pen, paths); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The thickness. - /// The shapes. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - float thickness, - IPathCollection paths) => - source.Draw(options, new Pen(brush, thickness), paths); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The brush. - /// The thickness. - /// The paths. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - IBrush brush, - float thickness, - IPathCollection paths) => - source.Draw(new Pen(brush, thickness), paths); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The thickness. - /// The paths. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - float thickness, - IPathCollection paths) => - source.Draw(options, new SolidBrush(color), thickness, paths); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The color. - /// The thickness. - /// The paths. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - Color color, - float thickness, - IPathCollection paths) => - source.Draw(new SolidBrush(color), thickness, paths); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs deleted file mode 100644 index dfe30f6a3c..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of polygon outlines to the type. - /// - public static class DrawPathExtensions - { - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The image this method extends. - /// The options. - /// The pen. - /// The path. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - IPen pen, - IPath path) => - source.Fill(options, pen.StrokeFill, new ShapePath(path, pen)); - - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The image this method extends. - /// The pen. - /// The path. - /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) => - source.Draw(GraphicsOptions.Default, pen, path); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The thickness. - /// The shape. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - float thickness, - IPath path) => - source.Draw(options, new Pen(brush, thickness), path); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The brush. - /// The thickness. - /// The path. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - IBrush brush, - float thickness, - IPath path) => - source.Draw(new Pen(brush, thickness), path); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The thickness. - /// The path. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - float thickness, - IPath path) => - source.Draw(options, new SolidBrush(color), thickness, path); - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The color. - /// The thickness. - /// The path. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - Color color, - float thickness, - IPath path) => - source.Draw(new SolidBrush(color), thickness, path); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs deleted file mode 100644 index 86d8e9e2e2..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of closed linear polygons to the type. - /// - public static class DrawPolygonExtensions - { - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawPolygon( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - float thickness, - params PointF[] points) => - source.Draw(options, new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The brush. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawPolygon( - this IImageProcessingContext source, - IBrush brush, - float thickness, - params PointF[] points) => - source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The color. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawPolygon( - this IImageProcessingContext source, - Color color, - float thickness, - params PointF[] points) => - source.DrawPolygon(new SolidBrush(color), thickness, points); - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The thickness. - /// The points. - /// The . - public static IImageProcessingContext DrawPolygon( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - float thickness, - params PointF[] points) => - source.DrawPolygon(options, new SolidBrush(color), thickness, points); - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided Pen. - /// - /// The image this method extends. - /// The pen. - /// The points. - /// The . - public static IImageProcessingContext DrawPolygon( - this IImageProcessingContext source, - IPen pen, - params PointF[] points) => - source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points))); - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided Pen. - /// - /// The image this method extends. - /// The options. - /// The pen. - /// The points. - /// The . - public static IImageProcessingContext DrawPolygon( - this IImageProcessingContext source, - GraphicsOptions options, - IPen pen, - params PointF[] points) => - source.Draw(options, pen, new Polygon(new LinearLineSegment(points))); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs deleted file mode 100644 index da78ab2ecc..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of rectangles to the type. - /// - public static class DrawRectangleExtensions - { - /// - /// Draws the outline of the rectangle with the provided pen. - /// - /// The image this method extends. - /// The options. - /// The pen. - /// The shape. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - IPen pen, - RectangleF shape) => - source.Draw(options, pen, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); - - /// - /// Draws the outline of the rectangle with the provided pen. - /// - /// The image this method extends. - /// The pen. - /// The shape. - /// The . - public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) => - source.Draw(GraphicsOptions.Default, pen, shape); - - /// - /// Draws the outline of the rectangle with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The thickness. - /// The shape. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - float thickness, - RectangleF shape) => - source.Draw(options, new Pen(brush, thickness), shape); - - /// - /// Draws the outline of the rectangle with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The brush. - /// The thickness. - /// The shape. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - IBrush brush, - float thickness, - RectangleF shape) => - source.Draw(new Pen(brush, thickness), shape); - - /// - /// Draws the outline of the rectangle with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The thickness. - /// The shape. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - float thickness, - RectangleF shape) => - source.Draw(options, new SolidBrush(color), thickness, shape); - - /// - /// Draws the outline of the rectangle with the provided brush at the provided thickness. - /// - /// The image this method extends. - /// The color. - /// The thickness. - /// The shape. - /// The . - public static IImageProcessingContext Draw( - this IImageProcessingContext source, - Color color, - float thickness, - RectangleF shape) => - source.Draw(new SolidBrush(color), thickness, shape); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs deleted file mode 100644 index 05cd3a1ae6..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Fonts; -using SixLabors.ImageSharp.Processing.Processors.Text; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the drawing of text to the type. - /// - public static class DrawTextExtensions - { - /// - /// Draws the text onto the the image filled via the brush. - /// - /// The image this method extends. - /// The text. - /// The font. - /// The color. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - string text, - Font font, - Color color, - PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, color, location); - - /// - /// Draws the text onto the the image filled via the brush. - /// - /// The image this method extends. - /// The options. - /// The text. - /// The font. - /// The color. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - TextGraphicsOptions options, - string text, - Font font, - Color color, - PointF location) => - source.DrawText(options, text, font, Brushes.Solid(color), null, location); - - /// - /// Draws the text onto the the image filled via the brush. - /// - /// The image this method extends. - /// The text. - /// The font. - /// The brush. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - string text, - Font font, - IBrush brush, - PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, brush, location); - - /// - /// Draws the text onto the the image filled via the brush. - /// - /// The image this method extends. - /// The options. - /// The text. - /// The font. - /// The brush. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - TextGraphicsOptions options, - string text, - Font font, - IBrush brush, - PointF location) => - source.DrawText(options, text, font, brush, null, location); - - /// - /// Draws the text onto the the image outlined via the pen. - /// - /// The image this method extends. - /// The text. - /// The font. - /// The pen. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - string text, - Font font, - IPen pen, - PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, pen, location); - - /// - /// Draws the text onto the the image outlined via the pen. - /// - /// The image this method extends. - /// The options. - /// The text. - /// The font. - /// The pen. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - TextGraphicsOptions options, - string text, - Font font, - IPen pen, - PointF location) => - source.DrawText(options, text, font, null, pen, location); - - /// - /// Draws the text onto the the image filled via the brush then outlined via the pen. - /// - /// The image this method extends. - /// The text. - /// The font. - /// The brush. - /// The pen. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - string text, - Font font, - IBrush brush, - IPen pen, - PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location); - - /// - /// Draws the text using the default resolution of 72dpi onto the the image filled via the brush then outlined via the pen. - /// - /// The image this method extends. - /// The options. - /// The text. - /// The font. - /// The brush. - /// The pen. - /// The location. - /// - /// The . - /// - public static IImageProcessingContext DrawText( - this IImageProcessingContext source, - TextGraphicsOptions options, - string text, - Font font, - IBrush brush, - IPen pen, - PointF location) => - source.ApplyProcessor(new DrawTextProcessor(options, text, font, brush, pen, location)); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs deleted file mode 100644 index 5de9c6d4ed..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the filling of polygons with various brushes to the type. - /// - public static class FillPathBuilderExtensions - { - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The graphics options. - /// The brush. - /// The shape. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - Action path) - { - var pb = new PathBuilder(); - path(pb); - - return source.Fill(options, brush, pb.Build()); - } - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The brush. - /// The path. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - IBrush brush, - Action path) => - source.Fill(GraphicsOptions.Default, brush, path); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The path. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - Action path) => - source.Fill(options, new SolidBrush(color), path); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The color. - /// The path. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - Color color, - Action path) => - source.Fill(new SolidBrush(color), path); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs deleted file mode 100644 index 776e1f7e4e..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the filling of collections of polygon outlines to the type. - /// - public static class FillPathCollectionExtensions - { - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The graphics options. - /// The brush. - /// The shapes. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - IPathCollection paths) - { - foreach (IPath s in paths) - { - source.Fill(options, brush, s); - } - - return source; - } - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The brush. - /// The paths. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - IBrush brush, - IPathCollection paths) => - source.Fill(GraphicsOptions.Default, brush, paths); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The paths. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - IPathCollection paths) => - source.Fill(options, new SolidBrush(color), paths); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The color. - /// The paths. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - Color color, - IPathCollection paths) => - source.Fill(new SolidBrush(color), paths); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs deleted file mode 100644 index 718016a9e6..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the filling of polygon outlines to the type. - /// - public static class FillPathExtensions - { - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The graphics options. - /// The brush. - /// The shape. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - IPath path) => - source.Fill(options, brush, new ShapeRegion(path)); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The image this method extends. - /// The brush. - /// The path. - /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) => - source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path)); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush.. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The path. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - IPath path) => - source.Fill(options, new SolidBrush(color), path); - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush.. - /// - /// The image this method extends. - /// The color. - /// The path. - /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, IPath path) => - source.Fill(new SolidBrush(color), path); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs deleted file mode 100644 index 9262c8baad..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the filling of closed linear polygons to the type. - /// - public static class FillPolygonExtensions - { - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The points. - /// The . - public static IImageProcessingContext FillPolygon( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - params PointF[] points) => - source.Fill(options, brush, new Polygon(new LinearLineSegment(points))); - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The image this method extends. - /// The brush. - /// The points. - /// The . - public static IImageProcessingContext FillPolygon( - this IImageProcessingContext source, - IBrush brush, - params PointF[] points) => - source.Fill(brush, new Polygon(new LinearLineSegment(points))); - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The image this method extends. - /// The options. - /// The color. - /// The points. - /// The . - public static IImageProcessingContext FillPolygon( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - params PointF[] points) => - source.Fill(options, new SolidBrush(color), new Polygon(new LinearLineSegment(points))); - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The image this method extends. - /// The color. - /// The points. - /// The . - public static IImageProcessingContext FillPolygon( - this IImageProcessingContext source, - Color color, - params PointF[] points) => - source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs deleted file mode 100644 index cfe37deb2c..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the filling of rectangles to the type. - /// - public static class FillRectangleExtensions - { - /// - /// Flood fills the image in the shape of the provided rectangle with the specified brush. - /// - /// The image this method extends. - /// The options. - /// The brush. - /// The shape. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - RectangleF shape) => - source.Fill(options, brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); - - /// - /// Flood fills the image in the shape of the provided rectangle with the specified brush. - /// - /// The image this method extends. - /// The brush. - /// The shape. - /// The . - public static IImageProcessingContext - Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) => - source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height)); - - /// - /// Flood fills the image in the shape of the provided rectangle with the specified brush. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The shape. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - RectangleF shape) => - source.Fill(options, new SolidBrush(color), shape); - - /// - /// Flood fills the image in the shape of the provided rectangle with the specified brush. - /// - /// The image this method extends. - /// The color. - /// The shape. - /// The . - public static IImageProcessingContext - Fill(this IImageProcessingContext source, Color color, RectangleF shape) => - source.Fill(new SolidBrush(color), shape); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs deleted file mode 100644 index 294e575140..0000000000 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing.Processors.Drawing; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds extensions that allow the filling of regions with various brushes to the type. - /// - public static class FillRegionExtensions - { - /// - /// Flood fills the image with the specified brush. - /// - /// The image this method extends. - /// The details how to fill the region of interest. - /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) => - source.Fill(GraphicsOptions.Default, brush); - - /// - /// Flood fills the image with the specified color. - /// - /// The image this method extends. - /// The color. - /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color) => - source.Fill(new SolidBrush(color)); - - /// - /// Flood fills the image with in the region with the specified brush. - /// - /// The image this method extends. - /// The brush. - /// The region. - /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) => - source.Fill(GraphicsOptions.Default, brush, region); - - /// - /// Flood fills the image with in the region with the specified color. - /// - /// The image this method extends. - /// The options. - /// The color. - /// The region. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - Color color, - Region region) => - source.Fill(options, new SolidBrush(color), region); - - /// - /// Flood fills the image with in the region with the specified color. - /// - /// The image this method extends. - /// The color. - /// The region. - /// The . - public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, Region region) => - source.Fill(new SolidBrush(color), region); - - /// - /// Flood fills the image with in the region with the specified brush. - /// - /// The image this method extends. - /// The graphics options. - /// The brush. - /// The region. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush, - Region region) => - source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); - - /// - /// Flood fills the image with the specified brush. - /// - /// The image this method extends. - /// The graphics options. - /// The details how to fill the region of interest. - /// The . - public static IImageProcessingContext Fill( - this IImageProcessingContext source, - GraphicsOptions options, - IBrush brush) => - source.ApplyProcessor(new FillProcessor(brush, options)); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/GradientBrush.cs b/src/ImageSharp.Drawing/Processing/GradientBrush.cs deleted file mode 100644 index 9826748c46..0000000000 --- a/src/ImageSharp.Drawing/Processing/GradientBrush.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.PixelFormats.PixelBlenders; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Base class for Gradient brushes - /// - public abstract class GradientBrush : IBrush - { - /// - /// Defines how the colors are repeated beyond the interval [0..1] - /// The gradient colors. - protected GradientBrush( - GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) - { - this.RepetitionMode = repetitionMode; - this.ColorStops = colorStops; - } - - /// - /// Gets how the colors are repeated beyond the interval [0..1]. - /// - protected GradientRepetitionMode RepetitionMode { get; } - - /// - /// Gets the list of color stops for this gradient. - /// - protected ColorStop[] ColorStops { get; } - - /// - public abstract BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) - where TPixel : struct, IPixel; - - /// - /// Base class for gradient brush applicators - /// - internal abstract class GradientBrushApplicator : BrushApplicator - where TPixel : struct, IPixel - { - private static readonly TPixel Transparent = Color.Transparent.ToPixel(); - - private readonly ColorStop[] colorStops; - - private readonly GradientRepetitionMode repetitionMode; - - /// - /// Initializes a new instance of the class. - /// - /// The target. - /// The options. - /// An array of color stops sorted by their position. - /// Defines if and how the gradient should be repeated. - protected GradientBrushApplicator( - ImageFrame target, - GraphicsOptions options, - ColorStop[] colorStops, - GradientRepetitionMode repetitionMode) - : base(target, options) - { - this.colorStops = colorStops; // TODO: requires colorStops to be sorted by position - should that be checked? - this.repetitionMode = repetitionMode; - } - - /// - /// Base implementation of the indexer for gradients - /// (follows the facade pattern, using abstract methods) - /// - /// X coordinate of the Pixel. - /// Y coordinate of the Pixel. - internal override TPixel this[int x, int y] - { - get - { - float positionOnCompleteGradient = this.PositionOnGradient(x + 0.5f, y + 0.5f); - - switch (this.repetitionMode) - { - case GradientRepetitionMode.None: - // do nothing. The following could be done, but is not necessary: - // onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient)); - break; - case GradientRepetitionMode.Repeat: - positionOnCompleteGradient = positionOnCompleteGradient % 1; - break; - case GradientRepetitionMode.Reflect: - positionOnCompleteGradient = positionOnCompleteGradient % 2; - if (positionOnCompleteGradient > 1) - { - positionOnCompleteGradient = 2 - positionOnCompleteGradient; - } - - break; - case GradientRepetitionMode.DontFill: - if (positionOnCompleteGradient > 1 || positionOnCompleteGradient < 0) - { - return Transparent; - } - - break; - default: - throw new ArgumentOutOfRangeException(); - } - - (ColorStop from, ColorStop to) = this.GetGradientSegment(positionOnCompleteGradient); - - if (from.Color.Equals(to.Color)) - { - return from.Color.ToPixel(); - } - else - { - var fromAsVector = from.Color.ToVector4(); - var toAsVector = to.Color.ToVector4(); - float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio); - - // TODO: this should be changeble for different gradienting functions - Vector4 result = PorterDuffFunctions.NormalSrcOver( - fromAsVector, - toAsVector, - onLocalGradient); - - TPixel resultColor = default; - resultColor.FromVector4(result); - return resultColor; - } - } - } - - /// - /// calculates the position on the gradient for a given point. - /// This method is abstract as it's content depends on the shape of the gradient. - /// - /// The x coordinate of the point - /// The y coordinate of the point - /// - /// The position the given point has on the gradient. - /// The position is not bound to the [0..1] interval. - /// Values outside of that interval may be treated differently, - /// e.g. for the enum. - /// - protected abstract float PositionOnGradient(float x, float y); - - private (ColorStop from, ColorStop to) GetGradientSegment( - float positionOnCompleteGradient) - { - ColorStop localGradientFrom = this.colorStops[0]; - ColorStop localGradientTo = default; - - // TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient) - foreach (ColorStop colorStop in this.colorStops) - { - localGradientTo = colorStop; - - if (colorStop.Ratio > positionOnCompleteGradient) - { - // we're done here, so break it! - break; - } - - localGradientFrom = localGradientTo; - } - - return (localGradientFrom, localGradientTo); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs b/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs deleted file mode 100644 index 6aed8a030c..0000000000 --- a/src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Modes to repeat a gradient. - /// - public enum GradientRepetitionMode - { - /// - /// don't repeat, keep the color of start and end beyond those points stable. - /// - None, - - /// - /// Repeat the gradient. - /// If it's a black-white gradient, with Repeat it will be Black->{gray}->White|Black->{gray}->White|... - /// - Repeat, - - /// - /// Reflect the gradient. - /// Similar to , but each other repetition uses inverse order of s. - /// Used on a Black-White gradient, Reflect leads to Black->{gray}->White->{gray}->White... - /// - Reflect, - - /// - /// With DontFill a gradient does not touch any pixel beyond it's borders. - /// For the this is beyond the orthogonal through start and end, - /// TODO For the cref="PolygonalGradientBrush" it's outside the polygon, - /// For and it's beyond 1.0. - /// - DontFill - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/IBrush.cs b/src/ImageSharp.Drawing/Processing/IBrush.cs deleted file mode 100644 index 0cd2e20fda..0000000000 --- a/src/ImageSharp.Drawing/Processing/IBrush.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 -{ - /// - /// Brush represents a logical configuration of a brush which can be used to source pixel colors - /// - /// - /// A brush is a simple class that will return an that will perform the - /// logic for retrieving pixel values for specific locations. - /// - public interface IBrush - { - /// - /// Creates the applicator for this brush. - /// - /// The pixel type. - /// The source image. - /// The region the brush will be applied to. - /// The graphic options - /// - /// The brush applicator for this brush - /// - /// - /// 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( - ImageFrame source, - RectangleF region, - GraphicsOptions options) - where TPixel : struct, IPixel; - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/IPen.cs b/src/ImageSharp.Drawing/Processing/IPen.cs deleted file mode 100644 index 0efcfc108c..0000000000 --- a/src/ImageSharp.Drawing/Processing/IPen.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Interface representing the pattern and size of the stroke to apply with a Pen. - /// - public interface IPen - { - /// - /// Gets the stroke fill. - /// - IBrush StrokeFill { get; } - - /// - /// Gets the width to apply to the stroke - /// - float StrokeWidth { get; } - - /// - /// Gets the stoke pattern. - /// - ReadOnlySpan StrokePattern { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs deleted file mode 100644 index 8485ddfd09..0000000000 --- a/src/ImageSharp.Drawing/Processing/ImageBrush.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Provides an implementation of an image brush for painting images within areas. - /// - public class ImageBrush : IBrush - { - /// - /// The image to paint. - /// - private readonly Image image; - - /// - /// Initializes a new instance of the class. - /// - /// The image. - public ImageBrush(Image image) - { - this.image = image; - } - - /// - public BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) - where TPixel : struct, IPixel - { - if (this.image is Image specificImage) - { - return new ImageBrushApplicator(source, specificImage, region, options, false); - } - - specificImage = this.image.CloneAs(); - - return new ImageBrushApplicator(source, specificImage, region, options, true); - } - - /// - /// The image brush applicator. - /// - private class ImageBrushApplicator : BrushApplicator - where TPixel : struct, IPixel - { - private ImageFrame sourceFrame; - - private Image sourceImage; - - private readonly bool shouldDisposeImage; - - /// - /// The y-length. - /// - private readonly int yLength; - - /// - /// The x-length. - /// - private readonly int xLength; - - /// - /// The Y offset. - /// - private readonly int offsetY; - - /// - /// The X offset. - /// - private readonly int offsetX; - - /// - /// Initializes a new instance of the class. - /// - /// The target image. - /// The image. - /// The region. - /// The options - /// Whether to dispose the image on disposal of the applicator. - public ImageBrushApplicator( - ImageFrame target, - Image image, - RectangleF region, - GraphicsOptions options, - bool shouldDisposeImage) - : base(target, options) - { - this.sourceImage = image; - this.sourceFrame = image.Frames.RootFrame; - this.shouldDisposeImage = shouldDisposeImage; - this.xLength = image.Width; - this.yLength = image.Height; - this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); - this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0); - } - - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// - internal override TPixel this[int x, int y] - { - get - { - int srcX = (x - this.offsetX) % this.xLength; - int srcY = (y - this.offsetY) % this.yLength; - return this.sourceFrame[srcX, srcY]; - } - } - - /// - public override void Dispose() - { - if (this.shouldDisposeImage) - { - this.sourceImage?.Dispose(); - this.sourceImage = null; - this.sourceFrame = null; - } - } - - /// - internal override void Apply(Span scanline, int x, int y) - { - // Create a span for colors - using (IMemoryOwner amountBuffer = this.Target.MemoryAllocator.Allocate(scanline.Length)) - using (IMemoryOwner overlay = this.Target.MemoryAllocator.Allocate(scanline.Length)) - { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); - - int sourceY = (y - this.offsetY) % this.yLength; - int offsetX = x - this.offsetX; - Span sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY); - - for (int i = 0; i < scanline.Length; i++) - { - amountSpan[i] = scanline[i] * this.Options.BlendPercentage; - - int sourceX = (i + offsetX) % this.xLength; - TPixel pixel = sourceRow[sourceX]; - overlaySpan[i] = pixel; - } - - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend( - this.sourceFrame.Configuration, - destinationRow, - destinationRow, - overlaySpan, - amountSpan); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs deleted file mode 100644 index bb99eeb26a..0000000000 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs +++ /dev/null @@ -1,161 +0,0 @@ -// 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 -{ - /// - /// Provides an implementation of a brush for painting linear gradients within areas. - /// Supported right now: - /// - a set of colors in relative distances to each other. - /// - public sealed class LinearGradientBrush : GradientBrush - { - private readonly PointF p1; - - private readonly PointF p2; - - /// - /// Initializes a new instance of the class. - /// - /// Start point - /// End point - /// defines how colors are repeated. - /// - public LinearGradientBrush( - PointF p1, - PointF p2, - GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) - : base(repetitionMode, colorStops) - { - this.p1 = p1; - this.p2 = p2; - } - - /// - public override BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) => - new LinearGradientBrushApplicator( - source, - this.p1, - this.p2, - this.ColorStops, - this.RepetitionMode, - options); - - /// - /// The linear gradient brush applicator. - /// - private sealed class LinearGradientBrushApplicator : GradientBrushApplicator - where TPixel : struct, IPixel - { - private readonly PointF start; - - private readonly PointF end; - - /// - /// the vector along the gradient, x component - /// - private readonly float alongX; - - /// - /// the vector along the gradient, y component - /// - private readonly float alongY; - - /// - /// the vector perpendicular to the gradient, y component - /// - private readonly float acrossY; - - /// - /// the vector perpendicular to the gradient, x component - /// - private readonly float acrossX; - - /// - /// the result of ^2 + ^2 - /// - private readonly float alongsSquared; - - /// - /// the length of the defined gradient (between source and end) - /// - private readonly float length; - - /// - /// Initializes a new instance of the class. - /// - /// The source - /// start point of the gradient - /// end point of the gradient - /// tuple list of colors and their respective position between 0 and 1 on the line - /// defines how the gradient colors are repeated. - /// the graphics options - public LinearGradientBrushApplicator( - ImageFrame source, - PointF start, - PointF end, - ColorStop[] colorStops, - GradientRepetitionMode repetitionMode, - GraphicsOptions options) - : base(source, options, colorStops, repetitionMode) - { - this.start = start; - this.end = end; - - // the along vector: - this.alongX = this.end.X - this.start.X; - this.alongY = this.end.Y - this.start.Y; - - // the cross vector: - this.acrossX = this.alongY; - this.acrossY = -this.alongX; - - // some helpers: - this.alongsSquared = (this.alongX * this.alongX) + (this.alongY * this.alongY); - this.length = MathF.Sqrt(this.alongsSquared); - } - - protected override float PositionOnGradient(float x, float y) - { - if (this.acrossX == 0) - { - return (x - this.start.X) / (this.end.X - this.start.X); - } - else if (this.acrossY == 0) - { - return (y - this.start.Y) / (this.end.Y - this.start.Y); - } - else - { - float deltaX = x - this.start.X; - float deltaY = y - this.start.Y; - float k = ((this.alongY * deltaX) - (this.alongX * deltaY)) / this.alongsSquared; - - // point on the line: - float x4 = x - (k * this.alongY); - float y4 = y + (k * this.alongX); - - // get distance from (x4,y4) to start - float distance = MathF.Sqrt(MathF.Pow(x4 - this.start.X, 2) + MathF.Pow(y4 - this.start.Y, 2)); - - // get and return ratio - float ratio = distance / this.length; - return ratio; - } - } - - public override void Dispose() - { - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs deleted file mode 100644 index 7315dc5a3e..0000000000 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ /dev/null @@ -1,287 +0,0 @@ -// 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.PixelFormats; -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Provides an implementation of a brush for painting gradients between multiple color positions in 2D coordinates. - /// It works similarly with the class in System.Drawing.Drawing2D of the same name. - /// - public sealed class PathGradientBrush : IBrush - { - private readonly IList edges; - - private readonly Color centerColor; - - /// - /// Initializes a new instance of the class. - /// - /// Points that constitute a polygon that represents the gradient area. - /// Array of colors that correspond to each point in the polygon. - /// Color at the center of the gradient area to which the other colors converge. - public PathGradientBrush(PointF[] points, Color[] colors, Color centerColor) - { - if (points == null) - { - throw new ArgumentNullException(nameof(points)); - } - - if (points.Length < 3) - { - throw new ArgumentOutOfRangeException( - nameof(points), - "There must be at least 3 lines to construct a path gradient brush."); - } - - if (colors == null) - { - throw new ArgumentNullException(nameof(colors)); - } - - if (!colors.Any()) - { - throw new ArgumentOutOfRangeException( - nameof(colors), - "One or more color is needed to construct a path gradient brush."); - } - - int size = points.Length; - - var lines = new ILineSegment[size]; - - for (int i = 0; i < size; i++) - { - lines[i] = new LinearLineSegment(points[i % size], points[(i + 1) % size]); - } - - this.centerColor = centerColor; - - Color ColorAt(int index) => colors[index % colors.Length]; - - this.edges = lines.Select(s => new Path(s)) - .Select((path, i) => new Edge(path, ColorAt(i), ColorAt(i + 1))).ToList(); - } - - /// - /// Initializes a new instance of the class. - /// - /// Points that constitute a polygon that represents the gradient area. - /// Array of colors that correspond to each point in the polygon. - public PathGradientBrush(PointF[] points, Color[] colors) - : this(points, colors, CalculateCenterColor(colors)) - { - } - - /// - public BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) - where TPixel : struct, IPixel - { - return new PathGradientBrushApplicator(source, this.edges, this.centerColor, options); - } - - private static Color CalculateCenterColor(Color[] colors) - { - if (colors == null) - { - throw new ArgumentNullException(nameof(colors)); - } - - if (!colors.Any()) - { - throw new ArgumentOutOfRangeException( - nameof(colors), - "One or more color is needed to construct a path gradient brush."); - } - - return new Color(colors.Select(c => c.ToVector4()).Aggregate((p1, p2) => p1 + p2) / colors.Length); - } - - private static float DistanceBetween(PointF p1, PointF p2) => ((Vector2)(p2 - p1)).Length(); - - private struct Intersection - { - public Intersection(PointF point, float distance) - { - this.Point = point; - this.Distance = distance; - } - - public PointF Point { get; } - - public float Distance { get; } - } - - /// - /// An edge of the polygon that represents the gradient area. - /// - private class Edge - { - private readonly Path path; - - private readonly float length; - - private readonly PointF[] buffer; - - public Edge(Path path, Color startColor, Color endColor) - { - this.path = path; - - Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray(); - - this.Start = points.First(); - this.StartColor = startColor.ToVector4(); - - this.End = points.Last(); - this.EndColor = endColor.ToVector4(); - - this.length = DistanceBetween(this.End, this.Start); - this.buffer = new PointF[this.path.MaxIntersections]; - } - - public PointF Start { get; } - - public Vector4 StartColor { get; } - - public PointF End { get; } - - public Vector4 EndColor { get; } - - public Intersection? FindIntersection(PointF start, PointF end) - { - int intersections = this.path.FindIntersections(start, end, this.buffer); - - if (intersections == 0) - { - return null; - } - - return this.buffer.Take(intersections) - .Select(p => new Intersection(point: p, distance: ((Vector2)(p - start)).LengthSquared())) - .Aggregate((min, current) => min.Distance > current.Distance ? current : min); - } - - public Vector4 ColorAt(float distance) - { - float ratio = this.length > 0 ? distance / this.length : 0; - - return Vector4.Lerp(this.StartColor, this.EndColor, ratio); - } - - public Vector4 ColorAt(PointF point) => this.ColorAt(DistanceBetween(point, this.Start)); - } - - /// - /// The path gradient brush applicator. - /// - private class PathGradientBrushApplicator : BrushApplicator - where TPixel : struct, IPixel - { - private readonly PointF center; - - private readonly Vector4 centerColor; - - private readonly float maxDistance; - - private readonly IList edges; - - /// - /// Initializes a new instance of the class. - /// - /// The source image. - /// Edges of the polygon. - /// Color at the center of the gradient area to which the other colors converge. - /// The options. - public PathGradientBrushApplicator( - ImageFrame source, - IList edges, - Color centerColor, - GraphicsOptions options) - : base(source, options) - { - this.edges = edges; - - PointF[] points = edges.Select(s => s.Start).ToArray(); - - this.center = points.Aggregate((p1, p2) => p1 + p2) / edges.Count; - this.centerColor = centerColor.ToVector4(); - - this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max(); - } - - /// - internal override TPixel this[int x, int y] - { - get - { - var point = new PointF(x, y); - - if (point == this.center) - { - return new Color(this.centerColor).ToPixel(); - } - - Vector2 direction = Vector2.Normalize(point - this.center); - - PointF end = point + (PointF)(direction * this.maxDistance); - - (Edge edge, Intersection? info) = this.FindIntersection(point, end); - - if (!info.HasValue) - { - return Color.Transparent.ToPixel(); - } - - PointF intersection = info.Value.Point; - - Vector4 edgeColor = edge.ColorAt(intersection); - - float length = DistanceBetween(intersection, this.center); - float ratio = length > 0 ? DistanceBetween(intersection, point) / length : 0; - - Vector4 color = Vector4.Lerp(edgeColor, this.centerColor, ratio); - - return new Color(color).ToPixel(); - } - } - - private (Edge edge, Intersection? info) FindIntersection(PointF start, PointF end) - { - (Edge edge, Intersection? info) closest = default; - - foreach (Edge edge in this.edges) - { - Intersection? intersection = edge.FindIntersection(start, end); - - if (!intersection.HasValue) - { - continue; - } - - if (closest.info == null || closest.info.Value.Distance > intersection.Value.Distance) - { - closest = (edge, intersection); - } - } - - return closest; - } - - /// - public override void Dispose() - { - } - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush.cs b/src/ImageSharp.Drawing/Processing/PatternBrush.cs deleted file mode 100644 index 1999af8a39..0000000000 --- a/src/ImageSharp.Drawing/Processing/PatternBrush.cs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Provides an implementation of a pattern brush for painting patterns. - /// - /// - /// The patterns that are used to create a custom pattern brush are made up of a repeating matrix of flags, - /// where each flag denotes whether to draw the foreground color or the background color. - /// so to create a new bool[,] with your flags - /// - /// For example if you wanted to create a diagonal line that repeat every 4 pixels you would use a pattern like so - /// 1000 - /// 0100 - /// 0010 - /// 0001 - /// - /// - /// or you want a horizontal stripe which is 3 pixels apart you would use a pattern like - /// 1 - /// 0 - /// 0 - /// - /// - public class PatternBrush : IBrush - { - /// - /// The pattern. - /// - private readonly DenseMatrix pattern; - private readonly DenseMatrix patternVector; - - /// - /// Initializes a new instance of the class. - /// - /// Color of the fore. - /// Color of the back. - /// The pattern. - public PatternBrush(Color foreColor, Color backColor, bool[,] pattern) - : this(foreColor, backColor, new DenseMatrix(pattern)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Color of the fore. - /// Color of the back. - /// The pattern. - internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix pattern) - { - var foreColorVector = foreColor.ToVector4(); - var backColorVector = backColor.ToVector4(); - this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); - this.patternVector = new DenseMatrix(pattern.Columns, pattern.Rows); - for (int i = 0; i < pattern.Data.Length; i++) - { - if (pattern.Data[i]) - { - this.pattern.Data[i] = foreColor; - this.patternVector.Data[i] = foreColorVector; - } - else - { - this.pattern.Data[i] = backColor; - this.patternVector.Data[i] = backColorVector; - } - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The brush. - internal PatternBrush(PatternBrush brush) - { - this.pattern = brush.pattern; - this.patternVector = brush.patternVector; - } - - /// - public BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) - where TPixel : struct, IPixel => - new PatternBrushApplicator( - source, - this.pattern.ToPixelMatrix(source.Configuration), - options); - - /// - /// The pattern brush applicator. - /// - private class PatternBrushApplicator : BrushApplicator - where TPixel : struct, IPixel - { - /// - /// The pattern. - /// - private readonly DenseMatrix pattern; - - /// - /// Initializes a new instance of the class. - /// - /// The source image. - /// The pattern. - /// The options - public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, GraphicsOptions options) - : base(source, options) - { - this.pattern = pattern; - } - - /// - /// Gets the color for a single pixel. - /// # - /// The x. - /// The y. - /// - /// The Color. - /// - internal override TPixel this[int x, int y] - { - get - { - x = x % this.pattern.Columns; - y = y % this.pattern.Rows; - - // 2d array index at row/column - return this.pattern[y, x]; - } - } - - /// - public override void Dispose() - { - // noop - } - - /// - internal override void Apply(Span scanline, int x, int y) - { - int patternY = y % this.pattern.Rows; - MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - - using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) - using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) - { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); - - for (int i = 0; i < scanline.Length; i++) - { - amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); - - int patternX = (x + i) % this.pattern.Columns; - overlaySpan[i] = this.pattern[patternY, patternX]; - } - - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend( - this.Target.Configuration, - destinationRow, - destinationRow, - overlaySpan, - amountSpan); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Pen.cs b/src/ImageSharp.Drawing/Processing/Pen.cs deleted file mode 100644 index ebad687d5a..0000000000 --- a/src/ImageSharp.Drawing/Processing/Pen.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Provides a pen that can apply a pattern to a line with a set brush and thickness - /// - /// - /// The pattern will be in to the form of new float[]{ 1f, 2f, 0.5f} this will be - /// converted into a pattern that is 3.5 times longer that the width with 3 sections - /// section 1 will be width long (making a square) and will be filled by the brush - /// section 2 will be width * 2 long and will be empty - /// section 3 will be width/2 long and will be filled - /// the the pattern will immediately repeat without gap. - /// - public class Pen : IPen - { - private readonly float[] pattern; - - /// - /// Initializes a new instance of the class. - /// - /// The color. - /// The width. - /// The pattern. - public Pen(Color color, float width, float[] pattern) - : this(new SolidBrush(color), width, pattern) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The brush. - /// The width. - /// The pattern. - public Pen(IBrush brush, float width, float[] pattern) - { - this.StrokeFill = brush; - this.StrokeWidth = width; - this.pattern = pattern; - } - - /// - /// Initializes a new instance of the class. - /// - /// The color. - /// The width. - public Pen(Color color, float width) - : this(new SolidBrush(color), width) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The brush. - /// The width. - public Pen(IBrush brush, float width) - : this(brush, width, Pens.EmptyPattern) - { - } - - /// - public IBrush StrokeFill { get; } - - /// - public float StrokeWidth { get; } - - /// - public ReadOnlySpan StrokePattern => this.pattern; - } -} diff --git a/src/ImageSharp.Drawing/Processing/Pens.cs b/src/ImageSharp.Drawing/Processing/Pens.cs deleted file mode 100644 index e60b5b6c7c..0000000000 --- a/src/ImageSharp.Drawing/Processing/Pens.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Contains a collection of common Pen styles - /// - public static class Pens - { - private static readonly float[] DashDotPattern = { 3f, 1f, 1f, 1f }; - private static readonly float[] DashDotDotPattern = { 3f, 1f, 1f, 1f, 1f, 1f }; - private static readonly float[] DottedPattern = { 1f, 1f }; - private static readonly float[] DashedPattern = { 3f, 1f }; - internal static readonly float[] EmptyPattern = new float[0]; - - /// - /// Create a solid pen with out any drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen Solid(Color color, float width) => new Pen(color, width); - - /// - /// Create a solid pen with out any drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen Solid(IBrush brush, float width) => new Pen(brush, width); - - /// - /// Create a pen with a 'Dash' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen Dash(Color color, float width) => new Pen(color, width, DashedPattern); - - /// - /// Create a pen with a 'Dash' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen Dash(IBrush brush, float width) => new Pen(brush, width, DashedPattern); - - /// - /// Create a pen with a 'Dot' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen Dot(Color color, float width) => new Pen(color, width, DottedPattern); - - /// - /// Create a pen with a 'Dot' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen Dot(IBrush brush, float width) => new Pen(brush, width, DottedPattern); - - /// - /// Create a pen with a 'Dash Dot' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen DashDot(Color color, float width) => new Pen(color, width, DashDotPattern); - - /// - /// Create a pen with a 'Dash Dot' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen DashDot(IBrush brush, float width) => new Pen(brush, width, DashDotPattern); - - /// - /// Create a pen with a 'Dash Dot Dot' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen DashDotDot(Color color, float width) => new Pen(color, width, DashDotDotPattern); - - /// - /// Create a pen with a 'Dash Dot Dot' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen DashDotDot(IBrush brush, float width) => new Pen(brush, width, DashDotDotPattern); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs deleted file mode 100644 index 1d3cf35576..0000000000 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ /dev/null @@ -1,43 +0,0 @@ -// 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.Drawing -{ - /// - /// Defines a processor to fill an with the given - /// using blending defined by the given . - /// - public class FillProcessor : IImageProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// The brush to use for filling. - /// The defining how to blend the brush pixels over the image pixels. - public FillProcessor(IBrush brush, GraphicsOptions options) - { - this.Brush = brush; - this.Options = options; - } - - /// - /// Gets the used for filling the destination image. - /// - public IBrush Brush { get; } - - /// - /// Gets the defining how to blend the brush pixels over the image pixels. - /// - public GraphicsOptions Options { get; } - - /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new FillProcessor(this, source, sourceRectangle); - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs deleted file mode 100644 index a7c22f6d7b..0000000000 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Using the brush as a source of pixels colors blends the brush color with source. - /// - /// The pixel format. - internal class FillProcessor : ImageProcessor - where TPixel : struct, IPixel - { - private readonly FillProcessor definition; - - public FillProcessor(FillProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) - { - this.definition = definition; - } - - /// - protected override void OnFrameApply(ImageFrame source) - { - Rectangle sourceRectangle = this.SourceRectangle; - Configuration configuration = this.Configuration; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - - // 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); - - int width = maxX - minX; - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - - IBrush brush = this.definition.Brush; - GraphicsOptions options = this.definition.Options; - - // If there's no reason for blending, then avoid it. - if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) - { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); - - TPixel colorPixel = solidBrush.Color.ToPixel(); - - ParallelHelper.IterateRows( - workingRect, - parallelSettings, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel); - } - }); - } - else - { - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) - using (BrushApplicator applicator = brush.CreateApplicator( - source, - sourceRectangle, - options)) - { - amount.GetSpan().Fill(1f); - - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - int offsetY = y - startY; - int offsetX = minX - startX; - - applicator.Apply(amount.GetSpan(), offsetX, offsetY); - } - }); - } - } - } - - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) - { - solidBrush = this.definition.Brush as SolidBrush; - - if (solidBrush == null) - { - return false; - } - - return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs deleted file mode 100644 index 2318f3168b..0000000000 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Defines a processor to fill pixels withing a given - /// with the given and blending defined by the given . - /// - public class FillRegionProcessor : IImageProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// The details how to fill the region of interest. - /// The region of interest to be filled. - /// The configuration options. - public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) - { - this.Region = region; - this.Brush = brush; - this.Options = options; - } - - /// - /// Gets the used for filling the destination image. - /// - public IBrush Brush { get; } - - /// - /// Gets the region that this processor applies to. - /// - public Region Region { get; } - - /// - /// Gets the defining how to blend the brush pixels over the image pixels. - /// - public GraphicsOptions Options { get; } - - /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new FillRegionProcessor(this, source, sourceRectangle); - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs deleted file mode 100644 index 45d5015ae0..0000000000 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Utils; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Using a brush and a shape fills shape with contents of brush the - /// - /// The type of the color. - /// - internal class FillRegionProcessor : ImageProcessor - where TPixel : struct, IPixel - { - private readonly FillRegionProcessor definition; - - public FillRegionProcessor(FillRegionProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) - { - this.definition = definition; - } - - /// - protected override void OnFrameApply(ImageFrame source) - { - Configuration configuration = this.Configuration; - GraphicsOptions options = this.definition.Options; - IBrush brush = this.definition.Brush; - Region region = this.definition.Region; - Rectangle rect = region.Bounds; - - // Align start/end positions. - int minX = Math.Max(0, rect.Left); - int maxX = Math.Min(source.Width, rect.Right); - int minY = Math.Max(0, rect.Top); - int maxY = Math.Min(source.Height, rect.Bottom); - if (minX >= maxX) - { - return; // no effect inside image; - } - - if (minY >= maxY) - { - return; // no effect inside image; - } - - 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 align with the pixel grid. - float offset = 0.5f; - if (options.Antialias) - { - offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset. - subpixelCount = options.AntialiasSubpixelDepth; - if (subpixelCount < 4) - { - subpixelCount = 4; - } - } - - using (BrushApplicator applicator = brush.CreateApplicator(source, rect, options)) - { - int scanlineWidth = maxX - minX; - using (IMemoryOwner bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) - using (IMemoryOwner bScanline = source.MemoryAllocator.Allocate(scanlineWidth)) - { - bool scanlineDirty = true; - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - - Span buffer = bBuffer.GetSpan(); - Span scanline = bScanline.GetSpan(); - - bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); - TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel() : default; - - for (int y = minY; y < maxY; y++) - { - if (scanlineDirty) - { - scanline.Clear(); - scanlineDirty = false; - } - - float yPlusOne = y + 1; - for (float subPixel = y; subPixel < yPlusOne; subPixel += subpixelFraction) - { - int pointsFound = region.Scan(subPixel + offset, buffer, configuration); - if (pointsFound == 0) - { - // nothing on this line, skip - continue; - } - - QuickSort.Sort(buffer.Slice(0, pointsFound)); - - for (int point = 0; point < pointsFound && point < buffer.Length - 1; point += 2) - { - // points will be paired up - float scanStart = buffer[point] - minX; - float scanEnd = buffer[point + 1] - minX; - int startX = (int)MathF.Floor(scanStart + offset); - int endX = (int)MathF.Floor(scanEnd + offset); - - if (startX >= 0 && startX < scanline.Length) - { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) - { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - if (endX >= 0 && endX < scanline.Length) - { - for (float x = endX; x < scanEnd; x += subpixelFraction) - { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - nextX = Math.Max(nextX, 0); - for (int x = nextX; x < endX; x++) - { - scanline[x] += subpixelFraction; - scanlineDirty = true; - } - } - } - - if (scanlineDirty) - { - if (!options.Antialias) - { - bool hasOnes = false; - bool hasZeros = false; - for (int x = 0; x < scanlineWidth; x++) - { - if (scanline[x] >= 0.5) - { - scanline[x] = 1; - hasOnes = true; - } - else - { - scanline[x] = 0; - hasZeros = true; - } - } - - if (isSolidBrushWithoutBlending && hasOnes != hasZeros) - { - if (hasOnes) - { - source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor); - } - - continue; - } - } - - applicator.Apply(scanline, minX, y); - } - } - } - } - } - - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) - { - solidBrush = this.definition.Brush as SolidBrush; - - if (solidBrush == null) - { - return false; - } - - return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs deleted file mode 100644 index 775cf55abf..0000000000 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.Fonts; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Text -{ - /// - /// Defines a processor to draw text on an . - /// - public class DrawTextProcessor : IImageProcessor - { - /// - /// Initializes a new instance of the class. - /// - /// The options - /// The text we want to render - /// The font we want to render with - /// The brush to source pixel colors from. - /// The pen to outline text with. - /// The location on the image to start drawing the text from. - public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location) - { - Guard.NotNull(text, nameof(text)); - Guard.NotNull(font, nameof(font)); - - if (brush is null && pen is null) - { - throw new ArgumentNullException($"Expected a {nameof(brush)} or {nameof(pen)}. Both were null"); - } - - this.Options = options; - this.Text = text; - this.Font = font; - this.Location = location; - this.Brush = brush; - this.Pen = pen; - } - - /// - /// Gets the brush used to fill the glyphs. - /// - public IBrush Brush { get; } - - /// - /// Gets the defining blending modes and text-specific drawing settings. - /// - public TextGraphicsOptions Options { get; } - - /// - /// Gets the text to draw. - /// - public string Text { get; } - - /// - /// Gets the pen used for outlining the text, if Null then we will not outline - /// - public IPen Pen { get; } - - /// - /// Gets the font used to render the text. - /// - public Font Font { get; } - - /// - /// Gets the location to draw the text at. - /// - public PointF Location { get; } - - /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new DrawTextProcessor(this, source, sourceRectangle); - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs deleted file mode 100644 index ea042635dd..0000000000 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Collections.Generic; - -using SixLabors.Fonts; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Utils; -using SixLabors.Memory; -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Processing.Processors.Text -{ - /// - /// Using the brush as a source of pixels colors blends the brush color with source. - /// - /// The pixel format. - internal class DrawTextProcessor : ImageProcessor - where TPixel : struct, IPixel - { - private CachingGlyphRenderer textRenderer; - - private readonly DrawTextProcessor definition; - - public DrawTextProcessor(DrawTextProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) - { - this.definition = definition; - } - - private TextGraphicsOptions Options => this.definition.Options; - - private Font Font => this.definition.Font; - - private PointF Location => this.definition.Location; - - private string Text => this.definition.Text; - - private IPen Pen => this.definition.Pen; - - private IBrush Brush => this.definition.Brush; - - protected override void BeforeImageApply() - { - base.BeforeImageApply(); - - // do everything at the image level as we are delegating the processing down to other processors - var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) - { - ApplyKerning = this.Options.ApplyKerning, - TabWidth = this.Options.TabWidth, - WrappingWidth = this.Options.WrapTextWidth, - HorizontalAlignment = this.Options.HorizontalAlignment, - VerticalAlignment = this.Options.VerticalAlignment - }; - - this.textRenderer = new CachingGlyphRenderer(this.Source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); - this.textRenderer.Options = (GraphicsOptions)this.Options; - var renderer = new TextRenderer(this.textRenderer); - renderer.RenderText(this.Text, style); - } - - protected override void AfterImageApply() - { - base.AfterImageApply(); - this.textRenderer?.Dispose(); - this.textRenderer = null; - } - - /// - protected override void OnFrameApply(ImageFrame source) - { - // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome - Draw(this.textRenderer.FillOperations, this.Brush); - Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill); - - void Draw(List operations, IBrush brush) - { - if (operations?.Count > 0) - { - using (BrushApplicator app = brush.CreateApplicator(source, this.SourceRectangle, this.textRenderer.Options)) - { - foreach (DrawingOperation operation in operations) - { - Buffer2D buffer = operation.Map; - int startY = operation.Location.Y; - int startX = operation.Location.X; - int offsetSpan = 0; - if (startX < 0) - { - offsetSpan = -startX; - startX = 0; - } - - if (startX >= source.Width) - { - continue; - } - - int firstRow = 0; - if (startY < 0) - { - firstRow = -startY; - } - - int maxHeight = source.Height - startY; - int end = Math.Min(operation.Map.Height, maxHeight); - - for (int row = firstRow; row < end; row++) - { - int y = startY + row; - Span span = buffer.GetRowSpan(row).Slice(offsetSpan); - app.Apply(span, startX, y); - } - } - } - } - } - } - - private struct DrawingOperation - { - public Buffer2D Map { get; set; } - - public Point Location { get; set; } - } - - private class CachingGlyphRenderer : IGlyphRenderer, IDisposable - { - // just enough accuracy to allow for 1/8 pixel differences which - // later are accumulated while rendering, but do not grow into full pixel offsets - // The value 8 is benchmarked to: - // - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant) - // - Cache hit ratio above 60% - private const float AccuracyMultiple = 8; - - private readonly PathBuilder builder; - - private Point currentRenderPosition; - private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams; - private readonly int offset; - private PointF currentPoint; - - private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> - glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>(); - - private readonly bool renderOutline; - private readonly bool renderFill; - private bool rasterizationRequired; - - public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill) - { - this.MemoryAllocator = memoryAllocator; - this.currentRenderPosition = default; - this.Pen = pen; - this.renderFill = renderFill; - this.renderOutline = pen != null; - this.offset = 2; - if (this.renderFill) - { - this.FillOperations = new List(size); - } - - if (this.renderOutline) - { - this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2); - this.OutlineOperations = new List(size); - } - - this.builder = new PathBuilder(); - } - - public List FillOperations { get; } - - public List OutlineOperations { get; } - - public MemoryAllocator MemoryAllocator { get; internal set; } - - public IPen Pen { get; internal set; } - - public GraphicsOptions Options { get; internal set; } - - public void BeginFigure() - { - this.builder.StartFigure(); - } - - public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters) - { - this.currentRenderPosition = Point.Truncate(bounds.Location); - PointF subPixelOffset = bounds.Location - this.currentRenderPosition; - - subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple; - subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple; - - // we have offset our rendering origin a little bit down to prevent edge cropping, move the draw origin up to compensate - this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset); - this.currentGlyphRenderParams = (parameters, subPixelOffset); - - if (this.glyphData.ContainsKey(this.currentGlyphRenderParams)) - { - // we have already drawn the glyph vectors skip trying again - this.rasterizationRequired = false; - return false; - } - - // we check to see if we have a render cache and if we do then we render else - this.builder.Clear(); - - // ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offset it back - this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset)); - - this.rasterizationRequired = true; - return true; - } - - public void BeginText(RectangleF bounds) - { - // not concerned about this one - this.OutlineOperations?.Clear(); - this.FillOperations?.Clear(); - } - - public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point) - { - this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point); - this.currentPoint = point; - } - - public void Dispose() - { - foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData) - { - kv.Value.Dispose(); - } - - this.glyphData.Clear(); - } - - public void EndFigure() - { - this.builder.CloseFigure(); - } - - public void EndGlyph() - { - GlyphRenderData renderData = default; - - // has the glyph been rendered already? - if (this.rasterizationRequired) - { - IPath path = this.builder.Build(); - - if (this.renderFill) - { - renderData.FillMap = this.Render(path); - } - - if (this.renderOutline) - { - if (this.Pen.StrokePattern.Length == 0) - { - path = path.GenerateOutline(this.Pen.StrokeWidth); - } - else - { - path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern); - } - - renderData.OutlineMap = this.Render(path); - } - - this.glyphData[this.currentGlyphRenderParams] = renderData; - } - else - { - renderData = this.glyphData[this.currentGlyphRenderParams]; - } - - if (this.renderFill) - { - this.FillOperations.Add(new DrawingOperation - { - Location = this.currentRenderPosition, - Map = renderData.FillMap - }); - } - - if (this.renderOutline) - { - this.OutlineOperations.Add(new DrawingOperation - { - Location = this.currentRenderPosition, - Map = renderData.OutlineMap - }); - } - } - - private Buffer2D Render(IPath path) - { - Size size = Rectangle.Ceiling(path.Bounds).Size; - size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2)); - - float subpixelCount = 4; - float offset = 0.5f; - if (this.Options.Antialias) - { - offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset. - subpixelCount = this.Options.AntialiasSubpixelDepth; - if (subpixelCount < 4) - { - subpixelCount = 4; - } - } - - // take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it. - Buffer2D fullBuffer = this.MemoryAllocator.Allocate2D(size.Width + 1, size.Height + 1, AllocationOptions.Clean); - - using (IMemoryOwner bufferBacking = this.MemoryAllocator.Allocate(path.MaxIntersections)) - using (IMemoryOwner rowIntersectionBuffer = this.MemoryAllocator.Allocate(size.Width)) - { - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - - for (int y = 0; y <= size.Height; y++) - { - Span scanline = fullBuffer.GetRowSpan(y); - bool scanlineDirty = false; - float yPlusOne = y + 1; - - for (float subPixel = y; subPixel < yPlusOne; subPixel += subpixelFraction) - { - var start = new PointF(path.Bounds.Left - 1, subPixel); - var end = new PointF(path.Bounds.Right + 1, subPixel); - Span intersectionSpan = rowIntersectionBuffer.GetSpan(); - Span buffer = bufferBacking.GetSpan(); - int pointsFound = path.FindIntersections(start, end, intersectionSpan); - - if (pointsFound == 0) - { - // nothing on this line skip - continue; - } - - for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++) - { - buffer[i] = intersectionSpan[i].X; - } - - QuickSort.Sort(buffer.Slice(0, pointsFound)); - - for (int point = 0; point < pointsFound; point += 2) - { - // points will be paired up - float scanStart = buffer[point]; - float scanEnd = buffer[point + 1]; - int startX = (int)MathF.Floor(scanStart + offset); - int endX = (int)MathF.Floor(scanEnd + offset); - - if (startX >= 0 && startX < scanline.Length) - { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) - { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - if (endX >= 0 && endX < scanline.Length) - { - for (float x = endX; x < scanEnd; x += subpixelFraction) - { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; - } - } - - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - nextX = Math.Max(nextX, 0); - for (int x = nextX; x < endX; x++) - { - scanline[x] += subpixelFraction; - scanlineDirty = true; - } - } - } - - if (scanlineDirty) - { - if (!this.Options.Antialias) - { - for (int x = 0; x < size.Width; x++) - { - if (scanline[x] >= 0.5) - { - scanline[x] = 1; - } - else - { - scanline[x] = 0; - } - } - } - } - } - } - - return fullBuffer; - } - - public void EndText() - { - } - - public void LineTo(PointF point) - { - this.builder.AddLine(this.currentPoint, point); - this.currentPoint = point; - } - - public void MoveTo(PointF point) - { - this.builder.StartFigure(); - this.currentPoint = point; - } - - public void QuadraticBezierTo(PointF secondControlPoint, PointF point) - { - this.builder.AddBezier(this.currentPoint, secondControlPoint, point); - this.currentPoint = point; - } - - private struct GlyphRenderData : IDisposable - { - public Buffer2D FillMap; - - public Buffer2D OutlineMap; - - public void Dispose() - { - this.FillMap?.Dispose(); - this.OutlineMap?.Dispose(); - } - } - } - } -} diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs deleted file mode 100644 index f4d2dd81f4..0000000000 --- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs +++ /dev/null @@ -1,104 +0,0 @@ -// 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 -{ - /// - /// A Circular Gradient Brush, defined by center point and radius. - /// - public sealed class RadialGradientBrush : GradientBrush - { - private readonly PointF center; - - private readonly float radius; - - /// - /// The center of the circular gradient and 0 for the color stops. - /// The radius of the circular gradient and 1 for the color stops. - /// Defines how the colors in the gradient are repeated. - /// the color stops as defined in base class. - public RadialGradientBrush( - PointF center, - float radius, - GradientRepetitionMode repetitionMode, - params ColorStop[] colorStops) - : base(repetitionMode, colorStops) - { - this.center = center; - this.radius = radius; - } - - /// - public override BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) => - new RadialGradientBrushApplicator( - source, - options, - this.center, - this.radius, - this.ColorStops, - this.RepetitionMode); - - /// - private sealed class RadialGradientBrushApplicator : GradientBrushApplicator - where TPixel : struct, IPixel - { - private readonly PointF center; - - private readonly float radius; - - /// - /// Initializes a new instance of the class. - /// - /// The target image - /// The options. - /// Center point of the gradient. - /// Radius of the gradient. - /// Definition of colors. - /// How the colors are repeated beyond the first gradient. - public RadialGradientBrushApplicator( - ImageFrame target, - GraphicsOptions options, - PointF center, - float radius, - ColorStop[] colorStops, - GradientRepetitionMode repetitionMode) - : base(target, options, colorStops, repetitionMode) - { - this.center = center; - this.radius = radius; - } - - /// - public override void Dispose() - { - } - - /// - /// As this is a circular gradient, the position on the gradient is based on - /// the distance of the point to the center. - /// - /// The X coordinate of the target pixel. - /// The Y coordinate of the target pixel. - /// the position on the color gradient. - protected override float PositionOnGradient(float x, float y) - { - float distance = MathF.Sqrt(MathF.Pow(this.center.X - x, 2) + MathF.Pow(this.center.Y - y, 2)); - return distance / this.radius; - } - - internal override void Apply(Span scanline, int x, int y) - { - // TODO: each row is symmetric across center, so we can calculate half of it and mirror it to improve performance. - base.Apply(scanline, x, y); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush.cs deleted file mode 100644 index fca95be327..0000000000 --- a/src/ImageSharp.Drawing/Processing/RecolorBrush.cs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Provides an implementation of a brush that can recolor an image - /// - public class RecolorBrush : IBrush - { - /// - /// Initializes a new instance of the class. - /// - /// Color of the source. - /// Color of the target. - /// The threshold as a value between 0 and 1. - public RecolorBrush(Color sourceColor, Color targetColor, float threshold) - { - this.SourceColor = sourceColor; - this.Threshold = threshold; - this.TargetColor = targetColor; - } - - /// - /// Gets the threshold. - /// - public float Threshold { get; } - - /// - /// Gets the source color. - /// - /// - /// The color of the source. - /// - public Color SourceColor { get; } - - /// - /// Gets the target color. - /// - public Color TargetColor { get; } - - /// - public BrushApplicator CreateApplicator( - ImageFrame source, - RectangleF region, - GraphicsOptions options) - where TPixel : struct, IPixel - { - return new RecolorBrushApplicator( - source, - this.SourceColor.ToPixel(), - this.TargetColor.ToPixel(), - this.Threshold, - options); - } - - /// - /// The recolor brush applicator. - /// - private class RecolorBrushApplicator : BrushApplicator - where TPixel : struct, IPixel - { - /// - /// The source color. - /// - private readonly Vector4 sourceColor; - - /// - /// The target color. - /// - private readonly Vector4 targetColor; - - /// - /// The threshold. - /// - private readonly float threshold; - - private readonly TPixel targetColorPixel; - - /// - /// Initializes a new instance of the class. - /// - /// The source image. - /// Color of the source. - /// Color of the target. - /// The threshold . - /// The options - public RecolorBrushApplicator(ImageFrame source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) - : base(source, options) - { - this.sourceColor = sourceColor.ToVector4(); - this.targetColor = targetColor.ToVector4(); - this.targetColorPixel = targetColor; - - // Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :) - var maxColor = default(TPixel); - maxColor.FromVector4(new Vector4(float.MaxValue)); - var minColor = default(TPixel); - minColor.FromVector4(new Vector4(float.MinValue)); - this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; - } - - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// - internal override TPixel this[int x, int y] - { - get - { - // Offset the requested pixel by the value in the rectangle (the shapes position) - TPixel result = this.Target[x, y]; - var background = result.ToVector4(); - float distance = Vector4.DistanceSquared(background, this.sourceColor); - if (distance <= this.threshold) - { - float lerpAmount = (this.threshold - distance) / this.threshold; - return this.Blender.Blend( - result, - this.targetColorPixel, - lerpAmount); - } - - return result; - } - } - - /// - public override void Dispose() - { - } - - /// - internal override void Apply(Span scanline, int x, int y) - { - MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - - using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) - using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) - { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); - - for (int i = 0; i < scanline.Length; i++) - { - amountSpan[i] = scanline[i] * this.Options.BlendPercentage; - - int offsetX = x + i; - - // No doubt this one can be optimized further but I can't imagine its - // actually being used and can probably be removed/internalized for now - overlaySpan[i] = this[offsetX, y]; - } - - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend( - this.Target.Configuration, - destinationRow, - destinationRow, - overlaySpan, - amountSpan); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush.cs b/src/ImageSharp.Drawing/Processing/SolidBrush.cs deleted file mode 100644 index c62566f6b7..0000000000 --- a/src/ImageSharp.Drawing/Processing/SolidBrush.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Provides an implementation of a solid brush for painting solid color areas. - /// - public class SolidBrush : IBrush - { - /// - /// The color to paint. - /// - private readonly Color color; - - /// - /// Initializes a new instance of the class. - /// - /// The color. - public SolidBrush(Color color) - { - this.color = color; - } - - /// - /// Gets the color. - /// - /// - /// The color. - /// - public Color Color => this.color; - - /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) - where TPixel : struct, IPixel - { - return new SolidBrushApplicator(source, this.color.ToPixel(), options); - } - - /// - /// The solid brush applicator. - /// - private class SolidBrushApplicator : BrushApplicator - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The source image. - /// The color. - /// The options - public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) - : base(source, options) - { - this.Colors = source.MemoryAllocator.Allocate(source.Width); - this.Colors.GetSpan().Fill(color); - } - - /// - /// Gets the colors. - /// - protected IMemoryOwner Colors { get; } - - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// - internal override TPixel this[int x, int y] => this.Colors.GetSpan()[x]; - - /// - public override void Dispose() - { - this.Colors.Dispose(); - } - - /// - internal override void Apply(Span scanline, int x, int y) - { - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x); - - // constrain the spans to each other - if (destinationRow.Length > scanline.Length) - { - destinationRow = destinationRow.Slice(0, scanline.Length); - } - else - { - scanline = scanline.Slice(0, destinationRow.Length); - } - - MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - Configuration configuration = this.Target.Configuration; - - if (this.Options.BlendPercentage == 1f) - { - this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); - } - else - { - using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) - { - Span amountSpan = amountBuffer.GetSpan(); - - for (int i = 0; i < scanline.Length; i++) - { - amountSpan[i] = scanline[i] * this.Options.BlendPercentage; - } - - this.Blender.Blend( - configuration, - destinationRow, - destinationRow, - this.Colors.GetSpan(), - amountSpan); - } - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs deleted file mode 100644 index 6c140be72e..0000000000 --- a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Fonts; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Options for influencing the drawing functions. - /// - public struct TextGraphicsOptions - { - private const int DefaultTextDpi = 72; - - /// - /// Represents the default . - /// - public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private bool? applyKerning; - - private float? tabWidth; - - private float? dpiX; - - private float? dpiY; - - private HorizontalAlignment? horizontalAlignment; - - private VerticalAlignment? verticalAlignment; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public TextGraphicsOptions(bool enableAntialiasing) - { - this.applyKerning = true; - this.tabWidth = 4; - this.WrapTextWidth = 0; - this.horizontalAlignment = HorizontalAlignment.Left; - this.verticalAlignment = VerticalAlignment.Top; - - this.antialiasSubpixelDepth = 16; - this.ColorBlendingMode = PixelColorBlendingMode.Normal; - this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialias = enableAntialiasing; - this.dpiX = DefaultTextDpi; - this.dpiY = DefaultTextDpi; - } - - /// - /// Gets or sets a value indicating whether antialiasing should be applied. - /// - public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } - - /// - /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. - /// - public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } - - /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation - /// - public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } - - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. - - /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation - /// - public PixelColorBlendingMode ColorBlendingMode { get; set; } - - /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation - /// - public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } - - /// - /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. - /// - public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } - - /// - /// Gets or sets a value indicating the number of space widths a tab should lock to. - /// - public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } - - /// - /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. - /// - public float WrapTextWidth { get; set; } - - /// - /// Gets or sets a value indicating the DPI to render text along the X axis. - /// - public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } - - /// - /// Gets or sets a value indicating the DPI to render text along the Y axis. - /// - public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } - - /// - /// Gets or sets a value indicating how to align the text relative to the rendering space. - /// If is greater than zero it will align relative to the space - /// defined by the location and width, if equals zero, and thus - /// wrapping disabled, then the alignment is relative to the drawing location. - /// - public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } - - /// - /// Gets or sets a value indicating how to align the text relative to the rendering space. - /// - public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } - - /// - /// Performs an implicit conversion from to . - /// - /// The options. - /// - /// The result of the conversion. - /// - public static implicit operator TextGraphicsOptions(GraphicsOptions options) - { - return new TextGraphicsOptions(options.Antialias) - { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - blendPercentage = options.BlendPercentage, - ColorBlendingMode = options.ColorBlendingMode, - AlphaCompositionMode = options.AlphaCompositionMode - }; - } - - /// - /// Performs an explicit conversion from to . - /// - /// The options. - /// - /// The result of the conversion. - /// - public static explicit operator GraphicsOptions(TextGraphicsOptions options) - { - return new GraphicsOptions(options.Antialias) - { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - ColorBlendingMode = options.ColorBlendingMode, - AlphaCompositionMode = options.AlphaCompositionMode, - BlendPercentage = options.BlendPercentage - }; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Utils/QuickSort.cs b/src/ImageSharp.Drawing/Utils/QuickSort.cs deleted file mode 100644 index ca1da5505a..0000000000 --- a/src/ImageSharp.Drawing/Utils/QuickSort.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Utils -{ - /// - /// Optimized quick sort implementation for Span{float} input - /// - internal class QuickSort - { - /// - /// Sorts the elements of in ascending order - /// - /// The items to sort - public static void Sort(Span data) - { - if (data.Length < 2) - { - return; - } - - if (data.Length == 2) - { - if (data[0] > data[1]) - { - Swap(ref data[0], ref data[1]); - } - - return; - } - - Sort(ref data[0], 0, data.Length - 1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Swap(ref float left, ref float right) - { - float tmp = left; - left = right; - right = tmp; - } - - private static void Sort(ref float data0, int lo, int hi) - { - if (lo < hi) - { - int p = Partition(ref data0, lo, hi); - Sort(ref data0, lo, p); - Sort(ref data0, p + 1, hi); - } - } - - private static int Partition(ref float data0, int lo, int hi) - { - float pivot = Unsafe.Add(ref data0, lo); - int i = lo - 1; - int j = hi + 1; - while (true) - { - do - { - i = i + 1; - } - while (Unsafe.Add(ref data0, i) < pivot && i < hi); - - do - { - j = j - 1; - } - while (Unsafe.Add(ref data0, j) > pivot && j > lo); - - if (i >= j) - { - return j; - } - - Swap(ref Unsafe.Add(ref data0, i), ref Unsafe.Add(ref data0, j)); - } - } - } -} diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 22e6d47e9a..9bf8943b71 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -6,7 +6,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Advanced { diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 1ceba5f90e..bb4ddb7d0c 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; @@ -19,6 +20,7 @@ namespace SixLabors.ImageSharp.Advanced /// None of the methods in this class should ever be called, the code only has to exist at compile-time to be picked up by the AoT compiler. /// (Very similar to the LinkerIncludes.cs technique used in Xamarin.Android projects.) /// + [ExcludeFromCodeCoverage] internal static class AotCompilerTools { static AotCompilerTools() @@ -40,7 +42,7 @@ namespace SixLabors.ImageSharp.Advanced /// private static void SeedEverything() { - Seed(); + Seed(); Seed(); Seed(); Seed(); @@ -48,8 +50,10 @@ namespace SixLabors.ImageSharp.Advanced Seed(); Seed(); Seed(); - Seed(); - Seed(); + Seed(); + Seed(); + Seed(); + Seed(); Seed(); Seed(); Seed(); @@ -136,7 +140,7 @@ namespace SixLabors.ImageSharp.Advanced TPixel pixel = default; using (var image = new ImageFrame(Configuration.Default, 1, 1)) { - test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0, 0); + test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0); } } diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs similarity index 54% rename from src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs index 40163bc789..f17d70a2a0 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs @@ -1,16 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Threading.Tasks; -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.ParallelUtils +namespace SixLabors.ImageSharp.Advanced.ParallelUtils { /// /// Defines execution settings for methods in . /// - internal readonly struct ParallelExecutionSettings + public readonly struct ParallelExecutionSettings { /// /// Default value for . @@ -20,11 +21,24 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Initializes a new instance of the struct. /// + /// The value used for initializing when using TPL. + /// The value for . + /// The . public ParallelExecutionSettings( int maxDegreeOfParallelism, int minimumPixelsProcessedPerTask, MemoryAllocator memoryAllocator) { + // Shall be compatible with ParallelOptions.MaxDegreeOfParallelism: + // https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions.maxdegreeofparallelism + if (maxDegreeOfParallelism == 0 || maxDegreeOfParallelism < -1) + { + throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism)); + } + + Guard.MustBeGreaterThan(minimumPixelsProcessedPerTask, 0, nameof(minimumPixelsProcessedPerTask)); + Guard.NotNull(memoryAllocator, nameof(memoryAllocator)); + this.MaxDegreeOfParallelism = maxDegreeOfParallelism; this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; this.MemoryAllocator = memoryAllocator; @@ -33,13 +47,15 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Initializes a new instance of the struct. /// + /// The value used for initializing when using TPL. + /// The . public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator) : this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator) { } /// - /// Gets the MemoryAllocator + /// Gets the . /// public MemoryAllocator MemoryAllocator { get; } @@ -60,12 +76,26 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Creates a new instance of /// having multiplied by /// + /// The value to multiply with. + /// The modified . public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier) { + Guard.MustBeGreaterThan(multiplier, 0, nameof(multiplier)); + return new ParallelExecutionSettings( this.MaxDegreeOfParallelism, this.MinimumPixelsProcessedPerTask * multiplier, this.MemoryAllocator); } + + /// + /// Get the default for a + /// + /// The . + /// The . + public static ParallelExecutionSettings FromConfiguration(Configuration configuration) + { + return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs similarity index 77% rename from src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs index 1e7299720d..4833dbafdb 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs @@ -7,32 +7,26 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; -using SixLabors.Primitives; -namespace SixLabors.ImageSharp.ParallelUtils +namespace SixLabors.ImageSharp.Advanced.ParallelUtils { /// /// Utility methods for batched processing of pixel row intervals. - /// Parallel execution is optimized for image processing. - /// Use this instead of direct calls! + /// Parallel execution is optimized for image processing based on values defined + /// or . + /// Using this class is preferred over direct usage of utility methods. /// - internal static class ParallelHelper + public static class ParallelHelper { - /// - /// Get the default for a - /// - public static ParallelExecutionSettings GetParallelSettings(this Configuration configuration) - { - return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); - } - /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// + /// The . + /// The to get the parallel settings from. + /// The method body defining the iteration logic on a single . public static void IterateRows(Rectangle rectangle, Configuration configuration, Action body) { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings(); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); IterateRows(rectangle, parallelSettings, body); } @@ -40,6 +34,9 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// + /// The . + /// The . + /// The method body defining the iteration logic on a single . public static void IterateRows( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, @@ -47,7 +44,9 @@ namespace SixLabors.ImageSharp.ParallelUtils { ValidateRectangle(rectangle); - int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); + int maxSteps = DivideCeil( + rectangle.Width * rectangle.Height, + parallelSettings.MinimumPixelsProcessedPerTask); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); @@ -81,7 +80,7 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action> body) @@ -133,13 +132,13 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, Configuration configuration, Action> body) where T : unmanaged { - IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body); + IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Color/Color.NamedColors.cs b/src/ImageSharp/Color/Color.NamedColors.cs index 8811516c1c..0575a3e99e 100644 --- a/src/ImageSharp/Color/Color.NamedColors.cs +++ b/src/ImageSharp/Color/Color.NamedColors.cs @@ -174,9 +174,9 @@ namespace SixLabors.ImageSharp public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// Represents a matching the W3C definition that has an hex value of #8FBC8F. /// - public static readonly Color DarkSeaGreen = FromRgba(143, 188, 139, 255); + public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, 255); /// /// Represents a matching the W3C definition that has an hex value of #483D8B. @@ -718,4 +718,4 @@ namespace SixLabors.ImageSharp /// public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 4bdbe088ca..5fad7a8e39 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] public static Color FromHex(string hex) { - Rgba32 rgba = Rgba32.FromHex(hex); + var rgba = Rgba32.FromHex(hex); return new Color(rgba); } @@ -133,8 +133,7 @@ namespace SixLabors.ImageSharp public override string ToString() => this.ToHex(); /// - /// Converts the color instance to an - /// implementation defined by . + /// Converts the color instance to a specified type. /// /// The pixel type to convert to. /// The pixel value. @@ -147,6 +146,24 @@ namespace SixLabors.ImageSharp return pixel; } + /// + /// Bulk converts a span of to a span of a specified type. + /// + /// The pixel type to convert to. + /// The configuration. + /// The source color span. + /// The destination pixel span. + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToPixel( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TPixel : struct, IPixel + { + ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); + PixelOperations.Instance.FromRgba64(configuration, rgba64Span, destination); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Color other) @@ -166,19 +183,5 @@ namespace SixLabors.ImageSharp { return this.data.PackedValue.GetHashCode(); } - - /// - /// Bulk convert a span of to a span of a specified pixel type. - /// - [MethodImpl(InliningOptions.ShortMethod)] - internal static void ToPixel( - Configuration configuration, - ReadOnlySpan source, - Span destination) - where TPixel : struct, IPixel - { - ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); - PixelOperations.Instance.FromRgba64(Configuration.Default, rgba64Span, destination); - } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs index 59c878485d..87aaa93a9f 100644 --- a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs +++ b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs @@ -1,7 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -#if !NETCOREAPP2_1 +#if !SUPPORTS_ENCODING_STRING using System; using System.Text; @@ -32,4 +32,4 @@ namespace SixLabors.ImageSharp } } } -#endif \ No newline at end of file +#endif diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index 6af09b220a..971bff3227 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -4,9 +4,7 @@ using System; using System.Buffers; using System.IO; - using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; namespace SixLabors.ImageSharp { @@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp /// internal static class StreamExtensions { -#if NETCOREAPP2_1 /// /// Writes data from a stream into the provided buffer. /// @@ -24,23 +21,18 @@ namespace SixLabors.ImageSharp /// The offset within the buffer to begin writing. /// The number of bytes to write to the stream. public static void Write(this Stream stream, Span buffer, int offset, int count) - { - stream.Write(buffer.Slice(offset, count)); - } + => stream.Write(buffer.Slice(offset, count)); /// /// Reads data from a stream into the provided buffer. /// /// The stream. - /// The buffer.. + /// The buffer. /// The offset within the buffer where the bytes are read into. /// The number of bytes, if available, to read. /// The actual number of bytes read. public static int Read(this Stream stream, Span buffer, int offset, int count) - { - return stream.Read(buffer.Slice(offset, count)); - } -#endif + => stream.Read(buffer.Slice(offset, count)); /// /// Skips the number of bytes in the given stream. @@ -75,17 +67,39 @@ namespace SixLabors.ImageSharp } public static void Read(this Stream stream, IManagedByteBuffer buffer) - { - stream.Read(buffer.Array, 0, buffer.Length()); - } + => stream.Read(buffer.Array, 0, buffer.Length()); public static void Write(this Stream stream, IManagedByteBuffer buffer) + => stream.Write(buffer.Array, 0, buffer.Length()); + +#if !SUPPORTS_SPAN_STREAM + // This is a port of the CoreFX implementation and is MIT Licensed: + // https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L742 + public static int Read(this Stream stream, Span buffer) { - stream.Write(buffer.Array, 0, buffer.Length()); + // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, + // in order to match the signature of the framework method that exists in + // .NET Core. + byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); + try + { + int numRead = stream.Read(sharedBuffer, 0, buffer.Length); + if ((uint)numRead > (uint)buffer.Length) + { + throw new IOException("Stream was too long."); + } + + new Span(sharedBuffer, 0, numRead).CopyTo(buffer); + return numRead; + } + finally + { + ArrayPool.Shared.Return(sharedBuffer); + } } -#if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0 - // This is a port of the CoreFX implementation and is MIT Licensed: https://github.com/dotnet/coreclr/blob/c4dca1072d15bdda64c754ad1ea474b1580fa554/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L770 + // This is a port of the CoreFX implementation and is MIT Licensed: + // https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L775 public static void Write(this Stream stream, ReadOnlySpan buffer) { // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, diff --git a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs index 0c22aa68ff..f827746015 100644 --- a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs +++ b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp { @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp } /// - /// Computes the sum of vectors in weighted by the kernel weight values. + /// Computes the sum of vectors in weighted by the kernel weight values and accumulates the partial results. /// /// The 1D convolution kernel. /// The source frame. @@ -73,16 +73,20 @@ namespace SixLabors.ImageSharp /// The maximum working area row. /// The minimum working area column. /// The maximum working area column. - public static void Convolve4( + /// The weight factor for the real component of the complex pixel values. + /// The weight factor for the imaginary component of the complex pixel values. + public static void Convolve4AndAccumulatePartials( Span kernel, Buffer2D sourceValues, - Span targetRow, + Span targetRow, int row, int column, int minRow, int maxRow, int minColumn, - int maxColumn) + int maxColumn, + float z, + float w) { ComplexVector4 vector = default; int kernelLength = kernel.Length; @@ -99,7 +103,7 @@ namespace SixLabors.ImageSharp vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX)); } - targetRow[column] = vector; + targetRow[column] += vector.WeightedSum(z, w); } } } diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs index c5c9ddebe1..ff6e3a4ec6 100644 --- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -6,7 +6,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/Common/Helpers/EnumUtils.cs b/src/ImageSharp/Common/Helpers/EnumUtils.cs new file mode 100644 index 0000000000..a98b7f84c6 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/EnumUtils.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Common utility methods for working with enums. + /// + internal static class EnumUtils + { + /// + /// Converts the numeric representation of the enumerated constants to an equivalent enumerated object. + /// + /// The type of enum + /// The value to parse + /// The default value to return. + /// The . + public static TEnum Parse(int value, TEnum defaultValue) + where TEnum : Enum + { + foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum))) + { + TEnum current = enumValue; + if (value == Unsafe.As(ref current)) + { + return enumValue; + } + } + + return defaultValue; + } + + /// + /// Returns a value indicating whether the given enum has a flag of the given value. + /// + /// The type of enum. + /// The value. + /// The flag. + /// The . + public static bool HasFlag(TEnum value, TEnum flag) + where TEnum : Enum + { + uint flagValue = Unsafe.As(ref flag); + return (Unsafe.As(ref value) & flagValue) == flagValue; + } + } +} diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 7460c9cac1..e7b14be420 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -1,11 +1,11 @@ -// Copyright (c) Six Labors and contributors. +// 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.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -14,6 +14,20 @@ namespace SixLabors.ImageSharp /// internal static class ImageMaths { + /// + /// Vector for converting pixel to gray value as specified by ITU-R Recommendation BT.709. + /// + private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f); + + /// + /// Convert a pixel value to grayscale using ITU-R Recommendation BT.709. + /// + /// The vector to get the luminance from. + /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels) + => (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1)); + /// /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709. /// @@ -23,7 +37,7 @@ namespace SixLabors.ImageSharp /// The . [MethodImpl(InliningOptions.ShortMethod)] public static byte Get8BitBT709Luminance(byte r, byte g, byte b) => - (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5f); + (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); /// /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709. @@ -34,7 +48,7 @@ namespace SixLabors.ImageSharp /// The . [MethodImpl(InliningOptions.ShortMethod)] public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) => - (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F)); + (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); /// /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709. @@ -45,7 +59,7 @@ namespace SixLabors.ImageSharp /// The . [MethodImpl(InliningOptions.ShortMethod)] public static ushort Get16BitBT709Luminance(float r, float g, float b) => - (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F)); + (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); /// /// Scales a value from a 16 bit to it's 8 bit equivalent. diff --git a/src/ImageSharp/Common/Helpers/TestHelpers.cs b/src/ImageSharp/Common/Helpers/TestHelpers.cs index d330233c4c..c6574e4b58 100644 --- a/src/ImageSharp/Common/Helpers/TestHelpers.cs +++ b/src/ImageSharp/Common/Helpers/TestHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Common.Helpers @@ -13,14 +13,18 @@ namespace SixLabors.ImageSharp.Common.Helpers /// Only intended to be used in tests! /// internal const string ImageSharpBuiltAgainst = -#if NET472 - "netfx4.7.2"; +#if NETCOREAPP3_1 + "netcoreapp3.1"; #elif NETCOREAPP2_1 "netcoreapp2.1"; +#elif NETSTANDARD2_1 + "netstandard2.1"; +#elif NETSTANDARD2_0 + "netstandard2.0"; #elif NETSTANDARD1_3 "netstandard1.3"; #else - "netstandard2.0"; + "net472"; #endif } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs index bef7eb1610..62b5644725 100644 --- a/src/ImageSharp/Common/Helpers/TolerantMath.cs +++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -103,4 +103,4 @@ namespace SixLabors.ImageSharp return Math.Floor(a); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs index 75dbb032c5..9e43061702 100644 --- a/src/ImageSharp/Common/Helpers/UnitConverter.cs +++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; @@ -86,9 +86,10 @@ namespace SixLabors.ImageSharp.Common.Helpers [MethodImpl(InliningOptions.ShortMethod)] public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile) { - return profile.TryGetValue(ExifTag.ResolutionUnit, out ExifValue resolution) - ? (PixelResolutionUnit)(byte)(((ushort)resolution.Value) - 1) // EXIF is 1, 2, 3 - : default; + IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit); + + // EXIF is 1, 2, 3 so we minus "1" off the result. + return resolution is null ? default : (PixelResolutionUnit)(byte)(resolution.Value - 1); } } } diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs index a4e0921d0a..594a5ff103 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs @@ -5,7 +5,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 0d44db8d87..9f26df300b 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -8,9 +8,10 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Processing; -using SixLabors.Memory; namespace SixLabors.ImageSharp { @@ -63,7 +64,7 @@ namespace SixLabors.ImageSharp get => this.maxDegreeOfParallelism; set { - if (value <= 0) + if (value == 0 || value < -1) { throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); } @@ -150,6 +151,7 @@ namespace SixLabors.ImageSharp /// /// /// . + /// . /// /// The default configuration of . internal static Configuration CreateDefaultInstance() @@ -158,7 +160,8 @@ namespace SixLabors.ImageSharp new PngConfigurationModule(), new JpegConfigurationModule(), new GifConfigurationModule(), - new BmpConfigurationModule()); + new BmpConfigurationModule(), + new TgaConfigurationModule()); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index b7733e0269..8d82d28fbc 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -11,7 +11,6 @@ using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Bmp { @@ -387,9 +386,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp if (rowHasUndefinedPixels) { // Slow path with undefined pixels. + int rowStartIdx = y * width * 3; for (int x = 0; x < width; x++) { - int idx = (y * width * 3) + (x * 3); + int idx = rowStartIdx + (x * 3); if (undefinedPixels[x, y]) { switch (this.options.RleSkippedPixelHandling) @@ -418,9 +418,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp else { // Fast path without any undefined pixels. + int rowStartIdx = y * width * 3; for (int x = 0; x < width; x++) { - int idx = (y * width * 3) + (x * 3); + int idx = rowStartIdx + (x * 3); color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); pixelRow[x] = color; } @@ -443,11 +444,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Keeps track of rows, which have undefined pixels. private void UncompressRle4(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { -#if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; -#else - var cmd = new byte[2]; -#endif int count = 0; while (count < buffer.Length) @@ -554,11 +551,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Keeps track of rows, which have undefined pixels. private void UncompressRle8(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { -#if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; -#else - var cmd = new byte[2]; -#endif int count = 0; while (count < buffer.Length) @@ -637,11 +630,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// Keeps track of rows, which have undefined pixels. private void UncompressRle24(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { -#if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; -#else - var cmd = new byte[2]; -#endif int uncompressedPixels = 0; while (uncompressedPixels < buffer.Length) @@ -1211,11 +1200,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private void ReadInfoHeader() { -#if NETCOREAPP2_1 Span buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize]; -#else - var buffer = new byte[BmpInfoHeader.MaxHeaderSize]; -#endif // Read the header size. this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); @@ -1319,7 +1304,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.metadata = meta; short bitsPerPixel = this.infoHeader.BitsPerPixel; - this.bmpMetadata = this.metadata.GetFormatMetadata(BmpFormat.Instance); + this.bmpMetadata = this.metadata.GetBmpMetadata(); this.bmpMetadata.InfoHeaderType = infoHeaderType; // We can only encode at these bit rates so far (1 bit and 4 bit are still missing). @@ -1337,11 +1322,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private void ReadFileHeader() { -#if NETCOREAPP2_1 Span buffer = stackalloc byte[BmpFileHeader.Size]; -#else - var buffer = new byte[BmpFileHeader.Size]; -#endif this.stream.Read(buffer, 0, BmpFileHeader.Size); short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer); diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index a5e1ee5dbc..1c7c606ca6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -12,7 +12,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Bmp { @@ -105,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; - BmpMetadata bmpMetadata = metadata.GetFormatMetadata(BmpFormat.Instance); + BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); this.bitsPerPixel = this.bitsPerPixel ?? bmpMetadata.BitsPerPixel; short bpp = (short)this.bitsPerPixel; @@ -173,11 +172,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp reserved: 0, offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize); -#if NETCOREAPP2_1 Span buffer = stackalloc byte[infoHeaderSize]; -#else - var buffer = new byte[infoHeaderSize]; -#endif fileHeader.WriteTo(buffer); stream.Write(buffer, 0, BmpFileHeader.Size); @@ -315,11 +310,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void Write8Bit(Stream stream, ImageFrame image) where TPixel : struct, IPixel { - bool isGray8 = typeof(TPixel) == typeof(Gray8); + bool isL8 = typeof(TPixel) == typeof(L8); using (IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize8Bit, AllocationOptions.Clean)) { Span colorPalette = colorPaletteBuffer.GetSpan(); - if (isGray8) + if (isL8) { this.Write8BitGray(stream, image, colorPalette); } diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index 3d7510bc2a..e1fc9ef1fe 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -22,9 +22,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp private bool IsSupportedFileFormat(ReadOnlySpan header) { - short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(header); - return header.Length >= this.HeaderSize && - (fileTypeMarker == BmpConstants.TypeMarkers.Bitmap || fileTypeMarker == BmpConstants.TypeMarkers.BitmapArray); + if (header.Length >= this.HeaderSize) + { + short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(header); + return fileTypeMarker == BmpConstants.TypeMarkers.Bitmap || fileTypeMarker == BmpConstants.TypeMarkers.BitmapArray; + } + + return false; } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs similarity index 100% rename from src/ImageSharp/Formats/Bmp/BmpMetaData.cs rename to src/ImageSharp/Formats/Bmp/BmpMetadata.cs diff --git a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs new file mode 100644 index 0000000000..0315b3c768 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class MetadataExtensions + { + /// + /// Gets the bmp format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(BmpFormat.Instance); + } +} diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index c11e93a93a..722c9c8992 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -10,8 +10,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Gif { @@ -553,7 +551,7 @@ namespace SixLabors.ImageSharp.Formats.Gif [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetFrameMetadata(ImageFrameMetadata meta) { - GifFrameMetadata gifMeta = meta.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata gifMeta = meta.GetGifMetadata(); if (this.graphicsControlExtension.DelayTime > 0) { gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime; @@ -615,7 +613,7 @@ namespace SixLabors.ImageSharp.Formats.Gif } this.metadata = meta; - this.gifMetadata = meta.GetFormatMetadata(GifFormat.Instance); + this.gifMetadata = meta.GetGifMetadata(); this.gifMetadata.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag ? GifColorTableMode.Global : GifColorTableMode.Local; diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index c8fc448125..b4aae0744b 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -12,7 +12,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Gif { @@ -78,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; - GifMetadata gifMetadata = metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata gifMetadata = metadata.GetGifMetadata(); this.colorTableMode = this.colorTableMode ?? gifMetadata.ColorTableMode; bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global; @@ -136,7 +135,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { ImageFrame frame = image.Frames[i]; ImageFrameMetadata metadata = frame.Metadata; - GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata frameMetadata = metadata.GetGifMetadata(); this.WriteGraphicalControlExtension(frameMetadata, transparencyIndex, stream); this.WriteImageDescriptor(frame, false, stream); @@ -166,7 +165,7 @@ namespace SixLabors.ImageSharp.Formats.Gif foreach (ImageFrame frame in image.Frames) { ImageFrameMetadata metadata = frame.Metadata; - GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata frameMetadata = metadata.GetGifMetadata(); if (quantized is null) { // Allow each frame to be encoded at whatever color depth the frame designates if set. diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs similarity index 100% rename from src/ImageSharp/Formats/Gif/GifFrameMetaData.cs rename to src/ImageSharp/Formats/Gif/GifFrameMetadata.cs diff --git a/src/ImageSharp/Formats/Gif/GifMetaData.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs similarity index 100% rename from src/ImageSharp/Formats/Gif/GifMetaData.cs rename to src/ImageSharp/Formats/Gif/GifMetadata.cs diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index af390e9545..0129db0e3d 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -8,7 +8,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Gif { @@ -113,11 +112,7 @@ namespace SixLabors.ImageSharp.Formats.Gif Unsafe.Add(ref suffixRef, code) = (byte)code; } -#if NETCOREAPP2_1 Span buffer = stackalloc byte[255]; -#else - var buffer = new byte[255]; -#endif while (xyz < length) { @@ -227,11 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if NETCOREAPP2_1 private int ReadBlock(Span buffer) -#else - private int ReadBlock(byte[] buffer) -#endif { int bufferSize = this.stream.ReadByte(); diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index d809106143..eda0c5fb8c 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -8,7 +8,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Gif { diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs new file mode 100644 index 0000000000..7c432d26fe --- /dev/null +++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class MetadataExtensions + { + /// + /// Gets the gif format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static GifMetadata GetGifMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(GifFormat.Instance); + + /// + /// Gets the gif format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(GifFormat.Instance); + } +} diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 8dafdac795..e8e84de7d8 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.IO; @@ -17,17 +17,16 @@ namespace SixLabors.ImageSharp.Formats /// The pixel format. /// The configuration for the image. /// The containing image data. - /// The decoded image of a given pixel type. + /// The . Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; /// /// Decodes the image from the specified stream to an . - /// The decoder is free to choose the pixel type. /// /// The configuration for the image. /// The containing image data. - /// The decoded image of a pixel type chosen by the decoder. + /// The . Image Decode(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index 13c89c82cf..34fe1aecbd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [MethodImpl(InliningOptions.ShortMethod)] public void CheckBits() { - if (this.remainingBits < 16) + if (this.remainingBits < JpegConstants.Huffman.MinBits) { this.FillBuffer(); } @@ -85,8 +85,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { // Attempt to load at least the minimum number of required bits into the buffer. // We fail to do so only if we hit a marker or reach the end of the input stream. - this.remainingBits += 48; - this.data = (this.data << 48) | this.GetBytes(); + this.remainingBits += JpegConstants.Huffman.FetchBits; + this.data = (this.data << JpegConstants.Huffman.FetchBits) | this.GetBytes(); } [MethodImpl(InliningOptions.ShortMethod)] @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private ulong GetBytes() { ulong temp = 0; - for (int i = 0; i < 6; i++) + for (int i = 0; i < JpegConstants.Huffman.FetchLoop; i++) { int b = this.ReadStream(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 4685ba2895..6025930169 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -82,12 +82,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Figure C.1: make table of Huffman code length for each symbol int p = 0; - for (int l = 1; l <= 16; l++) + for (int j = 1; j <= 16; j++) { - int i = this.Sizes[l]; + int i = this.Sizes[j]; while (i-- != 0) { - huffSize[p++] = (char)l; + huffSize[p++] = (char)j; } } @@ -111,20 +111,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Figure F.15: generate decoding tables for bit-sequential decoding p = 0; - for (int l = 1; l <= 16; l++) + for (int j = 1; j <= 16; j++) { - if (this.Sizes[l] != 0) + if (this.Sizes[j] != 0) { - int offset = p - (int)huffCode[p]; - this.ValOffset[l] = offset; - p += this.Sizes[l]; - this.MaxCode[l] = huffCode[p - 1]; // Maximum code of length l - this.MaxCode[l] <<= 64 - l; // Left justify - this.MaxCode[l] |= (1ul << (64 - l)) - 1; + this.ValOffset[j] = p - (int)huffCode[p]; + p += this.Sizes[j]; + this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l + this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify + this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1; } else { - this.MaxCode[l] = 0; + this.MaxCode[j] = 0; } } @@ -142,11 +141,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder p = 0; for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++) { + int jShift = JpegConstants.Huffman.LookupBits - length; for (int i = 1; i <= this.Sizes[length]; i++, p++) { // length = current code's length, p = its index in huffCode[] & Values[]. // Generate left-justified code followed by all possible bit sequences - int lookBits = (int)(huffCode[p] << (JpegConstants.Huffman.LookupBits - length)); + int lookBits = (int)(huffCode[p] << jShift); for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--) { this.LookaheadSize[lookBits] = (byte)length; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs index 2492a985a8..169b02e9fb 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index 2f393fadae..8075fd4bab 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -3,8 +3,6 @@ using System; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index c5efb812e0..44f9048a59 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index 5353303947..622c34e9bb 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -1,11 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { @@ -22,11 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.Frame = frame; this.Id = id; - // Valid sampling factors are 1..2 - if (horizontalFactor == 0 - || verticalFactor == 0 - || horizontalFactor > 2 - || verticalFactor > 2) + // Validate sampling factors. + if (horizontalFactor == 0 || verticalFactor == 0) { JpegThrowHelper.ThrowBadSampling(); } @@ -138,4 +133,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.SpectralBlocks = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index e1a9380a03..39c8be3120 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -6,8 +6,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index f3f2952b1d..0400978d2f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -7,8 +7,6 @@ using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; using JpegColorConverter = SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters.JpegColorConverter; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder diff --git a/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs b/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs index 48ad188561..94771aa64c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/SizeExtensions.cs @@ -4,8 +4,6 @@ using System; using System.Numerics; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Formats.Jpeg.Components { /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs index a39480e126..9f50e2cab1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; namespace SixLabors.ImageSharp.Formats.Jpeg { @@ -249,6 +250,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public const int RegisterSize = 64; + /// + /// The number of bits to fetch when filling the buffer. + /// + public const int FetchBits = 48; + + /// + /// The number of times to read the input stream when filling the buffer. + /// + public const int FetchLoop = FetchBits / 8; + + /// + /// The minimum number of bits allowed before by the before fetching. + /// + public const int MinBits = RegisterSize - FetchBits; + /// /// If the next Huffman code is no more than this number of bits, we can obtain its length /// and the corresponding symbol directly from this tables. @@ -266,4 +282,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public const int LookupSize = 1 << LookupBits; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index a1bf048521..4e1c0c1beb 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.IO; @@ -28,6 +28,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } } + /// + public Image Decode(Configuration configuration, Stream stream) + => this.Decode(configuration, stream); + /// public IImageInfo Identify(Configuration configuration, Stream stream) { @@ -38,8 +42,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return decoder.Identify(stream); } } - - /// - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index c15fe5e274..9b6a72cc97 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -15,9 +15,6 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg { @@ -465,24 +462,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } } - private double GetExifResolutionValue(ExifTag tag) + private double GetExifResolutionValue(ExifTag tag) { - if (!this.Metadata.ExifProfile.TryGetValue(tag, out ExifValue exifValue)) - { - return 0; - } + IExifValue resolution = this.Metadata.ExifProfile.GetValue(tag); - switch (exifValue.DataType) - { - case ExifDataType.Rational: - return ((Rational)exifValue.Value).ToDouble(); - case ExifDataType.Long: - return (uint)exifValue.Value; - case ExifDataType.DoubleFloat: - return (double)exifValue.Value; - default: - return 0; - } + return resolution is null ? 0 : resolution.Value.ToDouble(); } /// @@ -649,48 +633,51 @@ namespace SixLabors.ImageSharp.Formats.Jpeg switch (quantizationTableSpec >> 4) { case 0: + { + // 8 bit values + if (remaining < 64) { - // 8 bit values - if (remaining < 64) - { - done = true; - break; - } + done = true; + break; + } - this.InputStream.Read(this.temp, 0, 64); - remaining -= 64; + this.InputStream.Read(this.temp, 0, 64); + remaining -= 64; - ref Block8x8F table = ref this.QuantizationTables[tableIndex]; - for (int j = 0; j < 64; j++) - { - table[j] = this.temp[j]; - } + ref Block8x8F table = ref this.QuantizationTables[tableIndex]; + for (int j = 0; j < 64; j++) + { + table[j] = this.temp[j]; } + } - break; + break; case 1: + { + // 16 bit values + if (remaining < 128) { - // 16 bit values - if (remaining < 128) - { - done = true; - break; - } + done = true; + break; + } - this.InputStream.Read(this.temp, 0, 128); - remaining -= 128; + this.InputStream.Read(this.temp, 0, 128); + remaining -= 128; - ref Block8x8F table = ref this.QuantizationTables[tableIndex]; - for (int j = 0; j < 64; j++) - { - table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]; - } + ref Block8x8F table = ref this.QuantizationTables[tableIndex]; + for (int j = 0; j < 64; j++) + { + table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]; } + } + + break; - break; default: + { JpegThrowHelper.ThrowBadQuantizationTable(); break; + } } if (done) @@ -774,7 +761,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg for (int i = 0; i < this.ComponentCount; i++) { byte hv = this.temp[index + 1]; - int h = hv >> 4; + int h = (hv >> 4) & 15; int v = hv & 15; if (maxH < h) diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 0b9eeb609b..cd3c19aa3e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg ImageMetadata metadata = image.Metadata; // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1. - int qlty = (this.quality ?? metadata.GetFormatMetadata(JpegFormat.Instance).Quality).Clamp(1, 100); + int qlty = (this.quality ?? metadata.GetJpegMetadata().Quality).Clamp(1, 100); this.subsample = this.subsample ?? (qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); // Convert from a quality rating to a scaling factor. @@ -647,7 +647,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private void WriteExifProfile(ExifProfile exifProfile) { - if (exifProfile is null) + if (exifProfile is null || exifProfile.Values.Count == 0) { return; } @@ -655,9 +655,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg const int MaxBytesApp1 = 65533; // 64k - 2 padding bytes const int MaxBytesWithExifId = 65527; // Max - 6 bytes for EXIF header. - byte[] data = exifProfile?.ToByteArray(); + byte[] data = exifProfile.ToByteArray(); - if (data is null || data.Length == 0) + if (data.Length == 0) { return; } @@ -998,4 +998,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.outputStream.Write(this.buffer, 0, 4); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs similarity index 100% rename from src/ImageSharp/Formats/Jpeg/JpegMetaData.cs rename to src/ImageSharp/Formats/Jpeg/JpegMetadata.cs diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs new file mode 100644 index 0000000000..53a9d2a35d --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class MetadataExtensions + { + /// + /// Gets the jpeg format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); + } +} diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs new file mode 100644 index 0000000000..762a6c40cb --- /dev/null +++ b/src/ImageSharp/Formats/Png/MetadataExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class MetadataExtensions + { + /// + /// Gets the png format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static PngMetadata GetPngMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PngFormat.Instance); + } +} diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index c75f9465af..7d8498ab75 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -1,7 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Png { @@ -10,12 +10,11 @@ namespace SixLabors.ImageSharp.Formats.Png /// internal readonly struct PngChunk { - public PngChunk(int length, PngChunkType type, IManagedByteBuffer data = null, uint crc = 0) + public PngChunk(int length, PngChunkType type, IManagedByteBuffer data = null) { this.Length = length; this.Type = type; this.Data = data; - this.Crc = crc; } /// @@ -38,20 +37,12 @@ namespace SixLabors.ImageSharp.Formats.Png /// public IManagedByteBuffer Data { get; } - /// - /// Gets a CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk, - /// including the chunk type code and chunk data fields, but not including the length field. - /// The CRC is always present, even for chunks containing no data - /// - public uint Crc { get; } - /// /// Gets a value indicating whether the given chunk is critical to decoding /// public bool IsCritical => this.Type == PngChunkType.Header || this.Type == PngChunkType.Palette || - this.Type == PngChunkType.Data || - this.Type == PngChunkType.End; + this.Type == PngChunkType.Data; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 037f648f0a..69b341c8d4 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -17,7 +17,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Png { @@ -149,7 +148,7 @@ namespace SixLabors.ImageSharp.Formats.Png where TPixel : struct, IPixel { var metadata = new ImageMetadata(); - PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance); + PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; this.currentStream.Skip(8); Image image = null; @@ -221,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (image is null) { - throw new ImageFormatException("PNG Image does not contain a data chunk"); + PngThrowHelper.ThrowNoData(); } return image; @@ -240,7 +239,7 @@ namespace SixLabors.ImageSharp.Formats.Png public IImageInfo Identify(Stream stream) { var metadata = new ImageMetadata(); - PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance); + PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; this.currentStream.Skip(8); try @@ -285,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.Width == 0 && this.header.Height == 0) { - throw new ImageFormatException("PNG Image does not contain a header chunk"); + PngThrowHelper.ThrowNoHeader(); } return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata); @@ -407,7 +406,8 @@ namespace SixLabors.ImageSharp.Formats.Png case PngColorType.RgbWithAlpha: return this.header.BitDepth * 4; default: - throw new NotSupportedException("Unsupported PNG color type"); + PngThrowHelper.ThrowNotSupportedColor(); + return -1; } } @@ -528,7 +528,8 @@ namespace SixLabors.ImageSharp.Formats.Png break; default: - throw new ImageFormatException("Unknown filter type."); + PngThrowHelper.ThrowUnknownFilter(); + break; } this.ProcessDefilteredScanline(scanlineSpan, image, pngMetadata); @@ -601,7 +602,8 @@ namespace SixLabors.ImageSharp.Formats.Png break; default: - throw new ImageFormatException("Unknown filter type."); + PngThrowHelper.ThrowUnknownFilter(); + break; } Span rowSpan = image.GetPixelRowSpan(this.currentRow); @@ -655,8 +657,8 @@ namespace SixLabors.ImageSharp.Formats.Png scanlineSpan, rowSpan, pngMetadata.HasTransparency, - pngMetadata.TransparentGray16.GetValueOrDefault(), - pngMetadata.TransparentGray8.GetValueOrDefault()); + pngMetadata.TransparentL16.GetValueOrDefault(), + pngMetadata.TransparentL8.GetValueOrDefault()); break; @@ -739,8 +741,8 @@ namespace SixLabors.ImageSharp.Formats.Png pixelOffset, increment, pngMetadata.HasTransparency, - pngMetadata.TransparentGray16.GetValueOrDefault(), - pngMetadata.TransparentGray8.GetValueOrDefault()); + pngMetadata.TransparentL16.GetValueOrDefault(), + pngMetadata.TransparentL8.GetValueOrDefault()); break; @@ -834,11 +836,11 @@ namespace SixLabors.ImageSharp.Formats.Png { if (this.header.BitDepth == 16) { - pngMetadata.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); + pngMetadata.TransparentL16 = new L16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); } else { - pngMetadata.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0)); + pngMetadata.TransparentL8 = new L8(ReadByteLittleEndian(alpha, 0)); } pngMetadata.HasTransparency = true; @@ -1119,13 +1121,9 @@ namespace SixLabors.ImageSharp.Formats.Png chunk = new PngChunk( length: length, type: type, - data: this.ReadChunkData(length), - crc: this.ReadChunkCrc()); + data: this.ReadChunkData(length)); - if (chunk.IsCritical) - { - this.ValidateChunk(chunk); - } + this.ValidateChunk(chunk); return true; } @@ -1136,6 +1134,11 @@ namespace SixLabors.ImageSharp.Formats.Png /// The . private void ValidateChunk(in PngChunk chunk) { + if (!chunk.IsCritical) + { + return; + } + Span chunkType = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); @@ -1144,31 +1147,34 @@ namespace SixLabors.ImageSharp.Formats.Png this.crc.Update(chunkType); this.crc.Update(chunk.Data.GetSpan()); - if (this.crc.Value != chunk.Crc) + uint crc = this.ReadChunkCrc(); + if (this.crc.Value != crc) { string chunkTypeName = Encoding.ASCII.GetString(chunkType); - - throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); + PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName); } } /// /// Reads the cycle redundancy chunk from the data. /// - /// - /// Thrown if the input stream is not valid or corrupt. - /// + [MethodImpl(InliningOptions.ShortMethod)] private uint ReadChunkCrc() { - return this.currentStream.Read(this.buffer, 0, 4) == 4 - ? BinaryPrimitives.ReadUInt32BigEndian(this.buffer) - : throw new ImageFormatException("Image stream is not valid!"); + uint crc = 0; + if (this.currentStream.Read(this.buffer, 0, 4) == 4) + { + crc = BinaryPrimitives.ReadUInt32BigEndian(this.buffer); + } + + return crc; } /// /// Skips the chunk data and the cycle redundancy chunk read from the data. /// /// The image format chunk. + [MethodImpl(InliningOptions.ShortMethod)] private void SkipChunkDataAndCrc(in PngChunk chunk) { this.currentStream.Skip(chunk.Length); @@ -1179,6 +1185,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// Reads the chunk data from the stream. /// /// The length of the chunk data to read. + [MethodImpl(InliningOptions.ShortMethod)] private IManagedByteBuffer ReadChunkData(int length) { // We rent the buffer here to return it afterwards in Decode() @@ -1195,11 +1202,20 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Thrown if the input stream is not valid. /// + [MethodImpl(InliningOptions.ShortMethod)] private PngChunkType ReadChunkType() { - return this.currentStream.Read(this.buffer, 0, 4) == 4 - ? (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer) - : throw new ImageFormatException("Invalid PNG data."); + if (this.currentStream.Read(this.buffer, 0, 4) == 4) + { + return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer); + } + else + { + PngThrowHelper.ThrowInvalidChunkType(); + + // The IDE cannot detect the throw here. + return default; + } } /// @@ -1208,6 +1224,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Whether the length was read. /// + [MethodImpl(InliningOptions.ShortMethod)] private bool TryReadChunkLength(out int result) { if (this.currentStream.Read(this.buffer, 0, 4) == 4) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index efe0f20694..7f4b5b93b3 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -17,7 +17,6 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Png { @@ -199,16 +198,16 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.use16Bit) { // 16 bit grayscale - using (IMemoryOwner luminanceBuffer = this.memoryAllocator.Allocate(rowSpan.Length)) + using (IMemoryOwner luminanceBuffer = this.memoryAllocator.Allocate(rowSpan.Length)) { - Span luminanceSpan = luminanceBuffer.GetSpan(); - ref Gray16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan); - PixelOperations.Instance.ToGray16(this.configuration, rowSpan, luminanceSpan); + Span luminanceSpan = luminanceBuffer.GetSpan(); + ref L16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan); + PixelOperations.Instance.ToL16(this.configuration, rowSpan, luminanceSpan); // Can't map directly to byte array as it's big-endian. for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2) { - Gray16 luminance = Unsafe.Add(ref luminanceRef, x); + L16 luminance = Unsafe.Add(ref luminanceRef, x); BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue); } } @@ -218,7 +217,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.bitDepth == 8) { // 8 bit grayscale - PixelOperations.Instance.ToGray8Bytes( + PixelOperations.Instance.ToL8Bytes( this.configuration, rowSpan, rawScanlineSpan, @@ -235,7 +234,7 @@ namespace SixLabors.ImageSharp.Formats.Png Span tempSpan = temp.GetSpan(); // We need to first create an array of luminance bytes then scale them down to the correct bit depth. - PixelOperations.Instance.ToGray8Bytes( + PixelOperations.Instance.ToL8Bytes( this.configuration, rowSpan, tempSpan, @@ -622,11 +621,13 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image metadata. private void WriteExifChunk(Stream stream, ImageMetadata meta) { - if (meta.ExifProfile?.Values.Count > 0) + if (meta.ExifProfile is null || meta.ExifProfile.Values.Count == 0) { - meta.SyncProfiles(); - this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray()); + return; } + + meta.SyncProfiles(); + this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray()); } /// @@ -699,7 +700,7 @@ namespace SixLabors.ImageSharp.Formats.Png { using (var memoryStream = new MemoryStream()) { - using (var deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) + using (var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, this.options.CompressionLevel)) { deflateStream.Write(textBytes); } @@ -761,15 +762,15 @@ namespace SixLabors.ImageSharp.Formats.Png } else if (pngMetadata.ColorType == PngColorType.Grayscale) { - if (pngMetadata.TransparentGray16.HasValue && this.use16Bit) + if (pngMetadata.TransparentL16.HasValue && this.use16Bit) { - BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentGray16.Value.PackedValue); + BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentL16.Value.PackedValue); this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } - else if (pngMetadata.TransparentGray8.HasValue) + else if (pngMetadata.TransparentL8.HasValue) { alpha.Clear(); - alpha[1] = pngMetadata.TransparentGray8.Value.PackedValue; + alpha[1] = pngMetadata.TransparentL8.Value.PackedValue; this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } } @@ -790,7 +791,7 @@ namespace SixLabors.ImageSharp.Formats.Png using (var memoryStream = new MemoryStream()) { - using (var deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) + using (var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, this.options.CompressionLevel)) { if (this.options.InterlaceMethod == PngInterlaceMode.Adam7) { diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs similarity index 94% rename from src/ImageSharp/Formats/Png/PngMetaData.cs rename to src/ImageSharp/Formats/Png/PngMetadata.cs index ec8779a59a..87a2080f0f 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.Gamma = other.Gamma; this.InterlaceMethod = other.InterlaceMethod; this.HasTransparency = other.HasTransparency; - this.TransparentGray8 = other.TransparentGray8; - this.TransparentGray16 = other.TransparentGray16; + this.TransparentL8 = other.TransparentL8; + this.TransparentL16 = other.TransparentL16; this.TransparentRgb24 = other.TransparentRgb24; this.TransparentRgb48 = other.TransparentRgb48; @@ -77,13 +77,13 @@ namespace SixLabors.ImageSharp.Formats.Png /// Gets or sets the 8 bit grayscale transparent color. /// This represents any color in an 8 bit grayscale encoded png that should be transparent. /// - public Gray8? TransparentGray8 { get; set; } + public L8? TransparentL8 { get; set; } /// /// Gets or sets the 16 bit grayscale transparent color. /// This represents any color in a 16 bit grayscale encoded png that should be transparent. /// - public Gray16? TransparentGray16 { get; set; } + public L16? TransparentL16 { get; set; } /// /// Gets or sets a value indicating whether the image contains a transparency chunk and markers were decoded. diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs index c23694951d..5f9d1de9c5 100644 --- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png ReadOnlySpan scanlineSpan, Span rowSpan, bool hasTrans, - Gray16 luminance16Trans, - Gray8 luminanceTrans) + L16 luminance16Trans, + L8 luminanceTrans) where TPixel : struct, IPixel { TPixel pixel = default; @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Png for (int x = 0, o = 0; x < header.Width; x++, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - pixel.FromGray16(new Gray16(luminance)); + pixel.FromL16(Unsafe.As(ref luminance)); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Png for (int x = 0; x < header.Width; x++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); - pixel.FromGray8(new Gray8(luminance)); + pixel.FromL8(Unsafe.As(ref luminance)); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -55,32 +55,28 @@ namespace SixLabors.ImageSharp.Formats.Png if (header.BitDepth == 16) { - Rgba64 rgba64 = default; + La32 source = default; for (int x = 0, o = 0; x < header.Width; x++, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; + source.L = luminance; + source.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; - pixel.FromRgba64(rgba64); + pixel.FromLa32(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } else { + La16 source = default; byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); - Rgba32 rgba32 = default; for (int x = 0; x < header.Width; x++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue; + source.L = luminance; + source.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue; - pixel.FromRgba32(rgba32); + pixel.FromLa16(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -93,8 +89,8 @@ namespace SixLabors.ImageSharp.Formats.Png int pixelOffset, int increment, bool hasTrans, - Gray16 luminance16Trans, - Gray8 luminanceTrans) + L16 luminance16Trans, + L8 luminanceTrans) where TPixel : struct, IPixel { TPixel pixel = default; @@ -109,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Png for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - pixel.FromGray16(new Gray16(luminance)); + pixel.FromL16(Unsafe.As(ref luminance)); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -118,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Png for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); - pixel.FromGray8(new Gray8(luminance)); + pixel.FromL8(Unsafe.As(ref luminance)); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -128,32 +124,28 @@ namespace SixLabors.ImageSharp.Formats.Png if (header.BitDepth == 16) { - Rgba64 rgba64 = default; + La32 source = default; for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) { ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; + source.L = luminance; + source.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; - pixel.FromRgba64(rgba64); + pixel.FromLa32(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } else { + La16 source = default; byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); - Rgba32 rgba32 = default; for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) { byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue; + source.L = luminance; + source.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue; - pixel.FromRgba32(rgba32); + pixel.FromLa16(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -173,35 +165,26 @@ namespace SixLabors.ImageSharp.Formats.Png if (header.BitDepth == 16) { - Rgba64 rgba64 = default; + La32 source = default; for (int x = 0, o = 0; x < header.Width; x++, o += 4) { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = alpha; + source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - pixel.FromRgba64(rgba64); + pixel.FromLa32(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } else { - Rgba32 rgba32 = default; + La16 source = default; for (int x = 0; x < header.Width; x++) { int offset = x * bytesPerPixel; - byte luminance = Unsafe.Add(ref scanlineSpanRef, offset); - byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); - - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = alpha; + source.L = Unsafe.Add(ref scanlineSpanRef, offset); + source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); - pixel.FromRgba32(rgba32); + pixel.FromLa16(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } @@ -223,34 +206,26 @@ namespace SixLabors.ImageSharp.Formats.Png if (header.BitDepth == 16) { - Rgba64 rgba64 = default; + La32 source = default; for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 4) { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = alpha; + source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - pixel.FromRgba64(rgba64); + pixel.FromLa32(source); Unsafe.Add(ref rowSpanRef, x) = pixel; } } else { - Rgba32 rgba32 = default; int offset = 0; + La16 source = default; for (int x = pixelOffset; x < header.Width; x += increment) { - byte luminance = Unsafe.Add(ref scanlineSpanRef, offset); - byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = alpha; + source.L = Unsafe.Add(ref scanlineSpanRef, offset); + source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); - pixel.FromRgba32(rgba32); + pixel.FromLa16(source); Unsafe.Add(ref rowSpanRef, x) = pixel; offset += bytesPerPixel; } @@ -403,12 +378,12 @@ namespace SixLabors.ImageSharp.Formats.Png } else { + Rgba32 rgba32 = default; ReadOnlySpan rgb24Span = MemoryMarshal.Cast(scanlineSpan); ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span); for (int x = 0; x < header.Width; x++) { ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x); - Rgba32 rgba32 = default; rgba32.Rgb = rgb24; rgba32.A = rgb24.Equals(rgb24Trans) ? byte.MinValue : byte.MaxValue; @@ -576,4 +551,4 @@ namespace SixLabors.ImageSharp.Formats.Png } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngThrowHelper.cs b/src/ImageSharp/Formats/Png/PngThrowHelper.cs new file mode 100644 index 0000000000..dd3a054641 --- /dev/null +++ b/src/ImageSharp/Formats/Png/PngThrowHelper.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Png +{ + /// + /// Cold path optimizations for throwing png format based exceptions. + /// + internal static class PngThrowHelper + { + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNoHeader() => throw new ImageFormatException("PNG Image does not contain a header chunk"); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNoData() => throw new ImageFormatException("PNG Image does not contain a data chunk"); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowInvalidChunkType() => throw new ImageFormatException("Invalid PNG data."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotSupportedColor() => new NotSupportedException("Unsupported PNG color type"); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowUnknownFilter() => throw new ImageFormatException("Unknown filter type."); + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs index a06983b9ed..c4dc82a4dc 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs @@ -1,8 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Zlib { @@ -112,7 +113,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(ReadOnlySpan data) { - // (By Per Bothner) + ref byte dataRef = ref MemoryMarshal.GetReference(data); uint s1 = this.checksum & 0xFFFF; uint s2 = this.checksum >> 16; @@ -133,8 +134,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib count -= n; while (--n >= 0) { - s1 = s1 + (uint)(data[offset++] & 0xff); - s2 = s2 + s1; + s1 += Unsafe.Add(ref dataRef, offset++); + s2 += s1; } s1 %= Base; @@ -144,4 +145,4 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib 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 d1588c384f..77355e908c 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs @@ -1,8 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Zlib { @@ -141,9 +142,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { this.crc ^= CrcSeed; + ref uint crcTableRef = ref MemoryMarshal.GetReference(CrcTable.AsSpan()); for (int i = 0; i < data.Length; i++) { - this.crc = CrcTable[(this.crc ^ data[i]) & 0xFF] ^ (this.crc >> 8); + this.crc = Unsafe.Add(ref crcTableRef, (int)((this.crc ^ data[i]) & 0xFF)) ^ (this.crc >> 8); } this.crc ^= CrcSeed; diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs b/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs new file mode 100644 index 0000000000..5f62b13c7f --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/DeflateThrowHelper.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ + internal static class DeflateThrowHelper + { + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowAlreadyFinished() => throw new InvalidOperationException("Finish() already called."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowAlreadyClosed() => throw new InvalidOperationException("Deflator already closed."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowUnknownCompression() => throw new InvalidOperationException("Unknown compression function."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotProcessed() => throw new InvalidOperationException("Old input was not completely processed."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNull(string name) => throw new ArgumentNullException(name); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowHeapViolated() => throw new InvalidOperationException("Huffman heap invariant violated."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNoDeflate() => throw new ImageFormatException("Cannot deflate all input."); + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/Deflater.cs b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs new file mode 100644 index 0000000000..7398b089bb --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/Deflater.cs @@ -0,0 +1,295 @@ +// 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.Png.Zlib +{ + /// + /// This class compresses input with the deflate algorithm described in RFC 1951. + /// It has several compression levels and three different strategies described below. + /// + internal sealed class Deflater : IDisposable + { + /// + /// The best and slowest compression level. This tries to find very + /// long and distant string repetitions. + /// + public const int BestCompression = 9; + + /// + /// The worst but fastest compression level. + /// + public const int BestSpeed = 1; + + /// + /// The default compression level. + /// + public const int DefaultCompression = -1; + + /// + /// This level won't compress at all but output uncompressed blocks. + /// + public const int NoCompression = 0; + + /// + /// The compression method. This is the only method supported so far. + /// There is no need to use this constant at all. + /// + public const int Deflated = 8; + + /// + /// Compression level. + /// + private int level; + + /// + /// The current state. + /// + private int state; + + private DeflaterEngine engine; + private bool isDisposed; + + private const int IsFlushing = 0x04; + private const int IsFinishing = 0x08; + private const int BusyState = 0x10; + private const int FlushingState = 0x14; + private const int FinishingState = 0x1c; + private const int FinishedState = 0x1e; + private const int ClosedState = 0x7f; + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator to use for buffer allocations. + /// The compression level, a value between NoCompression and BestCompression. + /// + /// if level is out of range. + public Deflater(MemoryAllocator memoryAllocator, int level) + { + if (level == DefaultCompression) + { + level = 6; + } + else if (level < NoCompression || level > BestCompression) + { + throw new ArgumentOutOfRangeException(nameof(level)); + } + + // TODO: Possibly provide DeflateStrategy as an option. + this.engine = new DeflaterEngine(memoryAllocator, DeflateStrategy.Default); + + this.SetLevel(level); + this.Reset(); + } + + /// + /// Compression Level as an enum for safer use + /// + public enum CompressionLevel + { + /// + /// The best and slowest compression level. This tries to find very + /// long and distant string repetitions. + /// + BestCompression = Deflater.BestCompression, + + /// + /// The worst but fastest compression level. + /// + BestSpeed = Deflater.BestSpeed, + + /// + /// The default compression level. + /// + DefaultCompression = Deflater.DefaultCompression, + + /// + /// This level won't compress at all but output uncompressed blocks. + /// + NoCompression = Deflater.NoCompression, + + /// + /// The compression method. This is the only method supported so far. + /// There is no need to use this constant at all. + /// + Deflated = Deflater.Deflated + } + + /// + /// Gets a value indicating whetherthe stream was finished and no more output bytes + /// are available. + /// + public bool IsFinished => (this.state == FinishedState) && this.engine.Pending.IsFlushed; + + /// + /// Gets a value indicating whether the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method can also return true when the stream + /// was finished. + /// + public bool IsNeedingInput => this.engine.NeedsInput(); + + /// + /// Resets the deflater. The deflater acts afterwards as if it was + /// just created with the same compression level and strategy as it + /// had before. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Reset() + { + this.state = BusyState; + this.engine.Pending.Reset(); + this.engine.Reset(); + } + + /// + /// Flushes the current input block. Further calls to Deflate() will + /// produce enough output to inflate everything in the current input + /// block. It is used by DeflaterOutputStream to implement Flush(). + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Flush() => this.state |= IsFlushing; + + /// + /// Finishes the deflater with the current input block. It is an error + /// to give more input after this method was called. This method must + /// be called to force all bytes to be flushed. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Finish() => this.state |= IsFlushing | IsFinishing; + + /// + /// Sets the data which should be compressed next. This should be + /// only called when needsInput indicates that more input is needed. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// + /// The buffer containing the input data. + /// The start of the data. + /// The number of data bytes of input. + /// + /// if the buffer was finished or if previous input is still pending. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void SetInput(byte[] input, int offset, int count) + { + if ((this.state & IsFinishing) != 0) + { + DeflateThrowHelper.ThrowAlreadyFinished(); + } + + this.engine.SetInput(input, offset, count); + } + + /// + /// Sets the compression level. There is no guarantee of the exact + /// position of the change, but if you call this when needsInput is + /// true the change of compression level will occur somewhere near + /// before the end of the so far given input. + /// + /// + /// the new compression level. + /// + public void SetLevel(int level) + { + if (level == DefaultCompression) + { + level = 6; + } + else if (level < NoCompression || level > BestCompression) + { + throw new ArgumentOutOfRangeException(nameof(level)); + } + + if (this.level != level) + { + this.level = level; + this.engine.SetLevel(level); + } + } + + /// + /// Deflates the current input block to the given array. + /// + /// Buffer to store the compressed data. + /// Offset into the output array. + /// The maximum number of bytes that may be stored. + /// + /// The number of compressed bytes added to the output, or 0 if either + /// or returns true or length is zero. + /// + public int Deflate(byte[] output, int offset, int length) + { + int origLength = length; + + if (this.state == ClosedState) + { + DeflateThrowHelper.ThrowAlreadyClosed(); + } + + while (true) + { + int count = this.engine.Pending.Flush(output, offset, length); + offset += count; + length -= count; + + if (length == 0 || this.state == FinishedState) + { + break; + } + + if (!this.engine.Deflate((this.state & IsFlushing) != 0, (this.state & IsFinishing) != 0)) + { + switch (this.state) + { + case BusyState: + // We need more input now + return origLength - length; + + case FlushingState: + if (this.level != NoCompression) + { + // We have to supply some lookahead. 8 bit lookahead + // is needed by the zlib inflater, and we must fill + // the next byte, so that all bits are flushed. + int neededbits = 8 + ((-this.engine.Pending.BitCount) & 7); + while (neededbits > 0) + { + // Write a static tree block consisting solely of an EOF: + this.engine.Pending.WriteBits(2, 10); + neededbits -= 10; + } + } + + this.state = BusyState; + break; + + case FinishingState: + this.engine.Pending.AlignToByte(); + this.state = FinishedState; + break; + } + } + } + + return origLength - length; + } + + /// + public void Dispose() + { + if (!this.isDisposed) + { + this.engine.Dispose(); + this.engine = null; + this.isDisposed = true; + } + + GC.SuppressFinalize(this); + } + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs new file mode 100644 index 0000000000..67e8c6900b --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterConstants.cs @@ -0,0 +1,151 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ + /// + /// This class contains constants used for deflation. + /// + public static class DeflaterConstants + { + /// + /// Set to true to enable debugging + /// + public const bool DEBUGGING = false; + + /// + /// Written to Zip file to identify a stored block + /// + public const int STORED_BLOCK = 0; + + /// + /// Identifies static tree in Zip file + /// + public const int STATIC_TREES = 1; + + /// + /// Identifies dynamic tree in Zip file + /// + public const int DYN_TREES = 2; + + /// + /// Header flag indicating a preset dictionary for deflation + /// + public const int PRESET_DICT = 0x20; + + /// + /// Sets internal buffer sizes for Huffman encoding + /// + public const int DEFAULT_MEM_LEVEL = 8; + + /// + /// Internal compression engine constant + /// + public const int MAX_MATCH = 258; + + /// + /// Internal compression engine constant + /// + public const int MIN_MATCH = 3; + + /// + /// Internal compression engine constant + /// + public const int MAX_WBITS = 15; + + /// + /// Internal compression engine constant + /// + public const int WSIZE = 1 << MAX_WBITS; + + /// + /// Internal compression engine constant + /// + public const int WMASK = WSIZE - 1; + + /// + /// Internal compression engine constant + /// + public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + + /// + /// Internal compression engine constant + /// + public const int HASH_SIZE = 1 << HASH_BITS; + + /// + /// Internal compression engine constant + /// + public const int HASH_MASK = HASH_SIZE - 1; + + /// + /// Internal compression engine constant + /// + public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + /// + /// Internal compression engine constant + /// + public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + + /// + /// Internal compression engine constant + /// + public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + /// + /// Internal compression engine constant + /// + public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + + /// + /// Internal compression engine constant + /// + public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE - 5); + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_STORED = 0; + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_FAST = 1; + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_SLOW = 2; + + /// + /// Internal compression engine constant + /// + public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; + + /// + /// Internal compression engine constant + /// + public static int[] MAX_LAZY = { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; + + /// + /// Internal compression engine constant + /// + public static int[] NICE_LENGTH = { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; + + /// + /// Internal compression engine constant + /// + public static int[] MAX_CHAIN = { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; + + /// + /// Internal compression engine constant + /// + public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs new file mode 100644 index 0000000000..7be794b5e7 --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterEngine.cs @@ -0,0 +1,859 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ + /// + /// Strategies for deflater + /// + public enum DeflateStrategy + { + /// + /// The default strategy + /// + Default = 0, + + /// + /// This strategy will only allow longer string repetitions. It is + /// useful for random data with a small character set. + /// + Filtered = 1, + + /// + /// This strategy will not look for string repetitions at all. It + /// only encodes with Huffman trees (which means, that more common + /// characters get a smaller encoding. + /// + HuffmanOnly = 2 + } + + // DEFLATE ALGORITHM: + // + // The uncompressed stream is inserted into the window array. When + // the window array is full the first half is thrown away and the + // second half is copied to the beginning. + // + // The head array is a hash table. Three characters build a hash value + // and they the value points to the corresponding index in window of + // the last string with this hash. The prev array implements a + // linked list of matches with the same hash: prev[index & WMASK] points + // to the previous index with the same hash. + // + + /// + /// Low level compression engine for deflate algorithm which uses a 32K sliding window + /// with secondary compression from Huffman/Shannon-Fano codes. + /// + internal sealed unsafe class DeflaterEngine : IDisposable + { + private const int TooFar = 4096; + + // Hash index of string to be inserted + private int insertHashIndex; + + private int matchStart; + + // Length of best match + private int matchLen; + + // Set if previous match exists + private bool prevAvailable; + + private int blockStart; + + /// + /// Points to the current character in the window. + /// + private int strstart; + + /// + /// lookahead is the number of characters starting at strstart in + /// window that are valid. + /// So window[strstart] until window[strstart+lookahead-1] are valid + /// characters. + /// + private int lookahead; + + /// + /// The current compression function. + /// + private int compressionFunction; + + /// + /// The input data for compression. + /// + private byte[] inputBuf; + + /// + /// The offset into inputBuf, where input data starts. + /// + private int inputOff; + + /// + /// The end offset of the input data. + /// + private int inputEnd; + + private readonly DeflateStrategy strategy; + private DeflaterHuffman huffman; + private bool isDisposed; + + /// + /// Hashtable, hashing three characters to an index for window, so + /// that window[index]..window[index+2] have this hash code. + /// Note that the array should really be unsigned short, so you need + /// to and the values with 0xFFFF. + /// + private IMemoryOwner headMemoryOwner; + private MemoryHandle headMemoryHandle; + private readonly Memory head; + private readonly short* pinnedHeadPointer; + + /// + /// prev[index & WMASK] points to the previous index that has the + /// same hash code as the string starting at index. This way + /// entries with the same hash code are in a linked list. + /// Note that the array should really be unsigned short, so you need + /// to and the values with 0xFFFF. + /// + private IMemoryOwner prevMemoryOwner; + private MemoryHandle prevMemoryHandle; + private readonly Memory prev; + private readonly short* pinnedPrevPointer; + + /// + /// This array contains the part of the uncompressed stream that + /// is of relevance. The current character is indexed by strstart. + /// + private IManagedByteBuffer windowMemoryOwner; + private MemoryHandle windowMemoryHandle; + private readonly byte[] window; + private readonly byte* pinnedWindowPointer; + + private int maxChain; + private int maxLazy; + private int niceLength; + private int goodLength; + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator to use for buffer allocations. + /// The deflate strategy to use. + public DeflaterEngine(MemoryAllocator memoryAllocator, DeflateStrategy strategy) + { + this.huffman = new DeflaterHuffman(memoryAllocator); + this.Pending = this.huffman.Pending; + this.strategy = strategy; + + // Create pinned pointers to the various buffers to allow indexing + // without bounds checks. + this.windowMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE); + this.window = this.windowMemoryOwner.Array; + this.windowMemoryHandle = this.windowMemoryOwner.Memory.Pin(); + this.pinnedWindowPointer = (byte*)this.windowMemoryHandle.Pointer; + + this.headMemoryOwner = memoryAllocator.Allocate(DeflaterConstants.HASH_SIZE); + this.head = this.headMemoryOwner.Memory; + this.headMemoryHandle = this.headMemoryOwner.Memory.Pin(); + this.pinnedHeadPointer = (short*)this.headMemoryHandle.Pointer; + + this.prevMemoryOwner = memoryAllocator.Allocate(DeflaterConstants.WSIZE); + this.prev = this.prevMemoryOwner.Memory; + this.prevMemoryHandle = this.prevMemoryOwner.Memory.Pin(); + this.pinnedPrevPointer = (short*)this.prevMemoryHandle.Pointer; + + // We start at index 1, to avoid an implementation deficiency, that + // we cannot build a repeat pattern at index 0. + this.blockStart = this.strstart = 1; + } + + /// + /// Gets the pending buffer to use. + /// + public DeflaterPendingBuffer Pending { get; } + + /// + /// Deflate drives actual compression of data + /// + /// True to flush input buffers + /// Finish deflation with the current input. + /// Returns true if progress has been made. + public bool Deflate(bool flush, bool finish) + { + bool progress = false; + do + { + this.FillWindow(); + bool canFlush = flush && (this.inputOff == this.inputEnd); + + switch (this.compressionFunction) + { + case DeflaterConstants.DEFLATE_STORED: + progress = this.DeflateStored(canFlush, finish); + break; + + case DeflaterConstants.DEFLATE_FAST: + progress = this.DeflateFast(canFlush, finish); + break; + + case DeflaterConstants.DEFLATE_SLOW: + progress = this.DeflateSlow(canFlush, finish); + break; + + default: + DeflateThrowHelper.ThrowUnknownCompression(); + break; + } + } + while (this.Pending.IsFlushed && progress); // repeat while we have no pending output and progress was made + return progress; + } + + /// + /// Sets input data to be deflated. Should only be called when + /// returns true + /// + /// The buffer containing input data. + /// The offset of the first byte of data. + /// The number of bytes of data to use as input. + public void SetInput(byte[] buffer, int offset, int count) + { + if (buffer is null) + { + DeflateThrowHelper.ThrowNull(nameof(buffer)); + } + + if (offset < 0) + { + DeflateThrowHelper.ThrowOutOfRange(nameof(offset)); + } + + if (count < 0) + { + DeflateThrowHelper.ThrowOutOfRange(nameof(count)); + } + + if (this.inputOff < this.inputEnd) + { + DeflateThrowHelper.ThrowNotProcessed(); + } + + int end = offset + count; + + // We want to throw an ArgumentOutOfRangeException early. + // The check is very tricky: it also handles integer wrap around. + if ((offset > end) || (end > buffer.Length)) + { + DeflateThrowHelper.ThrowOutOfRange(nameof(count)); + } + + this.inputBuf = buffer; + this.inputOff = offset; + this.inputEnd = end; + } + + /// + /// Determines if more input is needed. + /// + /// Return true if input is needed via SetInput + [MethodImpl(InliningOptions.ShortMethod)] + public bool NeedsInput() => this.inputEnd == this.inputOff; + + /// + /// Reset internal state + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Reset() + { + this.huffman.Reset(); + this.blockStart = this.strstart = 1; + this.lookahead = 0; + this.prevAvailable = false; + this.matchLen = DeflaterConstants.MIN_MATCH - 1; + this.head.Span.Slice(0, DeflaterConstants.HASH_SIZE).Clear(); + this.prev.Span.Slice(0, DeflaterConstants.WSIZE).Clear(); + } + + /// + /// Set the deflate level (0-9) + /// + /// The value to set the level to. + public void SetLevel(int level) + { + if ((level < 0) || (level > 9)) + { + DeflateThrowHelper.ThrowOutOfRange(nameof(level)); + } + + this.goodLength = DeflaterConstants.GOOD_LENGTH[level]; + this.maxLazy = DeflaterConstants.MAX_LAZY[level]; + this.niceLength = DeflaterConstants.NICE_LENGTH[level]; + this.maxChain = DeflaterConstants.MAX_CHAIN[level]; + + if (DeflaterConstants.COMPR_FUNC[level] != this.compressionFunction) + { + switch (this.compressionFunction) + { + case DeflaterConstants.DEFLATE_STORED: + if (this.strstart > this.blockStart) + { + this.huffman.FlushStoredBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); + this.blockStart = this.strstart; + } + + this.UpdateHash(); + break; + + case DeflaterConstants.DEFLATE_FAST: + if (this.strstart > this.blockStart) + { + this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); + this.blockStart = this.strstart; + } + + break; + + case DeflaterConstants.DEFLATE_SLOW: + if (this.prevAvailable) + { + this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart - 1] & 0xFF); + } + + if (this.strstart > this.blockStart) + { + this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false); + this.blockStart = this.strstart; + } + + this.prevAvailable = false; + this.matchLen = DeflaterConstants.MIN_MATCH - 1; + break; + } + + this.compressionFunction = DeflaterConstants.COMPR_FUNC[level]; + } + } + + /// + /// Fill the window + /// + public void FillWindow() + { + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + if (this.strstart >= DeflaterConstants.WSIZE + DeflaterConstants.MAX_DIST) + { + this.SlideWindow(); + } + + // If there is not enough lookahead, but still some input left, read in the input. + if (this.lookahead < DeflaterConstants.MIN_LOOKAHEAD && this.inputOff < this.inputEnd) + { + int more = (2 * DeflaterConstants.WSIZE) - this.lookahead - this.strstart; + + if (more > this.inputEnd - this.inputOff) + { + more = this.inputEnd - this.inputOff; + } + + Array.Copy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more); + + this.inputOff += more; + this.lookahead += more; + } + + if (this.lookahead >= DeflaterConstants.MIN_MATCH) + { + this.UpdateHash(); + } + } + + /// + public void Dispose() + { + if (!this.isDisposed) + { + this.huffman.Dispose(); + + this.windowMemoryHandle.Dispose(); + this.windowMemoryOwner.Dispose(); + + this.headMemoryHandle.Dispose(); + this.headMemoryOwner.Dispose(); + + this.prevMemoryHandle.Dispose(); + this.prevMemoryOwner.Dispose(); + + this.windowMemoryOwner = null; + this.headMemoryOwner = null; + this.prevMemoryOwner = null; + this.huffman = null; + + this.isDisposed = true; + } + + GC.SuppressFinalize(this); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private void UpdateHash() + { + byte* pinned = this.pinnedWindowPointer; + this.insertHashIndex = (pinned[this.strstart] << DeflaterConstants.HASH_SHIFT) ^ pinned[this.strstart + 1]; + } + + /// + /// Inserts the current string in the head hash and returns the previous + /// value for this hash. + /// + /// The previous hash value + [MethodImpl(InliningOptions.ShortMethod)] + private int InsertString() + { + short match; + int hash = ((this.insertHashIndex << DeflaterConstants.HASH_SHIFT) ^ this.pinnedWindowPointer[this.strstart + (DeflaterConstants.MIN_MATCH - 1)]) & DeflaterConstants.HASH_MASK; + + short* pinnedHead = this.pinnedHeadPointer; + this.pinnedPrevPointer[this.strstart & DeflaterConstants.WMASK] = match = pinnedHead[hash]; + pinnedHead[hash] = unchecked((short)this.strstart); + this.insertHashIndex = hash; + return match & 0xFFFF; + } + + private void SlideWindow() + { + Unsafe.CopyBlockUnaligned(ref this.window[0], ref this.window[DeflaterConstants.WSIZE], DeflaterConstants.WSIZE); + this.matchStart -= DeflaterConstants.WSIZE; + this.strstart -= DeflaterConstants.WSIZE; + this.blockStart -= DeflaterConstants.WSIZE; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). + short* pinnedHead = this.pinnedHeadPointer; + for (int i = 0; i < DeflaterConstants.HASH_SIZE; ++i) + { + int m = pinnedHead[i] & 0xFFFF; + pinnedHead[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); + } + + // Slide the prev table. + short* pinnedPrev = this.pinnedPrevPointer; + for (int i = 0; i < DeflaterConstants.WSIZE; i++) + { + int m = pinnedPrev[i] & 0xFFFF; + pinnedPrev[i] = (short)(m >= DeflaterConstants.WSIZE ? (m - DeflaterConstants.WSIZE) : 0); + } + } + + /// + /// + /// Find the best (longest) string in the window matching the + /// string starting at strstart. + /// + /// + /// Preconditions: + /// + /// strstart + DeflaterConstants.MAX_MATCH <= window.length. + /// + /// + /// The current match. + /// True if a match greater than the minimum length is found + private bool FindLongestMatch(int curMatch) + { + int match; + int scan = this.strstart; + + // scanMax is the highest position that we can look at + int scanMax = scan + Math.Min(DeflaterConstants.MAX_MATCH, this.lookahead) - 1; + int limit = Math.Max(scan - DeflaterConstants.MAX_DIST, 0); + + int chainLength = this.maxChain; + int niceLength = Math.Min(this.niceLength, this.lookahead); + + int matchStrt = this.matchStart; + this.matchLen = Math.Max(this.matchLen, DeflaterConstants.MIN_MATCH - 1); + int matchLength = this.matchLen; + + if (scan + matchLength > scanMax) + { + return false; + } + + byte* pinnedWindow = this.pinnedWindowPointer; + int scanStart = this.strstart; + byte scanEnd1 = pinnedWindow[scan + matchLength - 1]; + byte scanEnd = pinnedWindow[scan + matchLength]; + + // Do not waste too much time if we already have a good match: + if (matchLength >= this.goodLength) + { + chainLength >>= 2; + } + + short* pinnedPrev = this.pinnedPrevPointer; + do + { + match = curMatch; + scan = scanStart; + + if (pinnedWindow[match + matchLength] != scanEnd + || pinnedWindow[match + matchLength - 1] != scanEnd1 + || pinnedWindow[match] != pinnedWindow[scan] + || pinnedWindow[++match] != pinnedWindow[++scan]) + { + continue; + } + + // scan is set to strstart+1 and the comparison passed, so + // scanMax - scan is the maximum number of bytes we can compare. + // below we compare 8 bytes at a time, so first we compare + // (scanMax - scan) % 8 bytes, so the remainder is a multiple of 8 + // n & (8 - 1) == n % 8. + switch ((scanMax - scan) & 7) + { + case 1: + if (pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + + case 2: + if (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + + case 3: + if (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + + case 4: + if (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + + case 5: + if (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + + case 6: + if (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + + case 7: + if (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]) + { + break; + } + + break; + } + + if (pinnedWindow[scan] == pinnedWindow[match]) + { + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart + 258 unless lookahead is + // exhausted first. + do + { + if (scan == scanMax) + { + ++scan; // advance to first position not matched + ++match; + + break; + } + } + while (pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match] + && pinnedWindow[++scan] == pinnedWindow[++match]); + } + + if (scan - scanStart > matchLength) + { + matchStrt = curMatch; + matchLength = scan - scanStart; + + if (matchLength >= niceLength) + { + break; + } + + scanEnd1 = pinnedWindow[scan - 1]; + scanEnd = pinnedWindow[scan]; + } + } + while ((curMatch = pinnedPrev[curMatch & DeflaterConstants.WMASK] & 0xFFFF) > limit && --chainLength != 0); + + this.matchStart = matchStrt; + this.matchLen = matchLength; + return matchLength >= DeflaterConstants.MIN_MATCH; + } + + private bool DeflateStored(bool flush, bool finish) + { + if (!flush && (this.lookahead == 0)) + { + return false; + } + + this.strstart += this.lookahead; + this.lookahead = 0; + + int storedLength = this.strstart - this.blockStart; + + if ((storedLength >= DeflaterConstants.MAX_BLOCK_SIZE) || // Block is full + (this.blockStart < DeflaterConstants.WSIZE && storedLength >= DeflaterConstants.MAX_DIST) || // Block may move out of window + flush) + { + bool lastBlock = finish; + if (storedLength > DeflaterConstants.MAX_BLOCK_SIZE) + { + storedLength = DeflaterConstants.MAX_BLOCK_SIZE; + lastBlock = false; + } + + this.huffman.FlushStoredBlock(this.window, this.blockStart, storedLength, lastBlock); + this.blockStart += storedLength; + return !(lastBlock || storedLength == 0); + } + + return true; + } + + private bool DeflateFast(bool flush, bool finish) + { + if (this.lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush) + { + return false; + } + + while (this.lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush) + { + if (this.lookahead == 0) + { + // We are flushing everything + this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish); + this.blockStart = this.strstart; + return false; + } + + if (this.strstart > (2 * DeflaterConstants.WSIZE) - DeflaterConstants.MIN_LOOKAHEAD) + { + // slide window, as FindLongestMatch needs this. + // This should only happen when flushing and the window + // is almost full. + this.SlideWindow(); + } + + int hashHead; + if (this.lookahead >= DeflaterConstants.MIN_MATCH && + (hashHead = this.InsertString()) != 0 && + this.strategy != DeflateStrategy.HuffmanOnly && + this.strstart - hashHead <= DeflaterConstants.MAX_DIST && + this.FindLongestMatch(hashHead)) + { + // longestMatch sets matchStart and matchLen + bool full = this.huffman.TallyDist(this.strstart - this.matchStart, this.matchLen); + + this.lookahead -= this.matchLen; + if (this.matchLen <= this.maxLazy && this.lookahead >= DeflaterConstants.MIN_MATCH) + { + while (--this.matchLen > 0) + { + ++this.strstart; + this.InsertString(); + } + + ++this.strstart; + } + else + { + this.strstart += this.matchLen; + if (this.lookahead >= DeflaterConstants.MIN_MATCH - 1) + { + this.UpdateHash(); + } + } + + this.matchLen = DeflaterConstants.MIN_MATCH - 1; + if (!full) + { + continue; + } + } + else + { + // No match found + this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart] & 0xff); + ++this.strstart; + --this.lookahead; + } + + if (this.huffman.IsFull()) + { + bool lastBlock = finish && (this.lookahead == 0); + this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, lastBlock); + this.blockStart = this.strstart; + return !lastBlock; + } + } + + return true; + } + + private bool DeflateSlow(bool flush, bool finish) + { + if (this.lookahead < DeflaterConstants.MIN_LOOKAHEAD && !flush) + { + return false; + } + + while (this.lookahead >= DeflaterConstants.MIN_LOOKAHEAD || flush) + { + if (this.lookahead == 0) + { + if (this.prevAvailable) + { + this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart - 1] & 0xff); + } + + this.prevAvailable = false; + + // We are flushing everything + this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish); + this.blockStart = this.strstart; + return false; + } + + if (this.strstart >= (2 * DeflaterConstants.WSIZE) - DeflaterConstants.MIN_LOOKAHEAD) + { + // slide window, as FindLongestMatch needs this. + // This should only happen when flushing and the window + // is almost full. + this.SlideWindow(); + } + + int prevMatch = this.matchStart; + int prevLen = this.matchLen; + if (this.lookahead >= DeflaterConstants.MIN_MATCH) + { + int hashHead = this.InsertString(); + + if (this.strategy != DeflateStrategy.HuffmanOnly && + hashHead != 0 && + this.strstart - hashHead <= DeflaterConstants.MAX_DIST && + this.FindLongestMatch(hashHead)) + { + // longestMatch sets matchStart and matchLen + // Discard match if too small and too far away + if (this.matchLen <= 5 && (this.strategy == DeflateStrategy.Filtered || (this.matchLen == DeflaterConstants.MIN_MATCH && this.strstart - this.matchStart > TooFar))) + { + this.matchLen = DeflaterConstants.MIN_MATCH - 1; + } + } + } + + // previous match was better + if ((prevLen >= DeflaterConstants.MIN_MATCH) && (this.matchLen <= prevLen)) + { + this.huffman.TallyDist(this.strstart - 1 - prevMatch, prevLen); + prevLen -= 2; + do + { + this.strstart++; + this.lookahead--; + if (this.lookahead >= DeflaterConstants.MIN_MATCH) + { + this.InsertString(); + } + } + while (--prevLen > 0); + + this.strstart++; + this.lookahead--; + this.prevAvailable = false; + this.matchLen = DeflaterConstants.MIN_MATCH - 1; + } + else + { + if (this.prevAvailable) + { + this.huffman.TallyLit(this.pinnedWindowPointer[this.strstart - 1] & 0xff); + } + + this.prevAvailable = true; + this.strstart++; + this.lookahead--; + } + + if (this.huffman.IsFull()) + { + int len = this.strstart - this.blockStart; + if (this.prevAvailable) + { + len--; + } + + bool lastBlock = finish && (this.lookahead == 0) && !this.prevAvailable; + this.huffman.FlushBlock(this.window, this.blockStart, len, lastBlock); + this.blockStart += len; + return !lastBlock; + } + } + + return true; + } + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs new file mode 100644 index 0000000000..e8dd8a5200 --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs @@ -0,0 +1,949 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ + /// + /// Performs Deflate Huffman encoding. + /// + internal sealed unsafe class DeflaterHuffman : IDisposable + { + private const int BufferSize = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + + // The number of literal codes. + private const int LiteralNumber = 286; + + // Number of distance codes + private const int DistanceNumber = 30; + + // Number of codes used to transfer bit lengths + private const int BitLengthNumber = 19; + + // Repeat previous bit length 3-6 times (2 bits of repeat count) + private const int Repeat3To6 = 16; + + // Repeat a zero length 3-10 times (3 bits of repeat count) + private const int Repeat3To10 = 17; + + // Repeat a zero length 11-138 times (7 bits of repeat count) + private const int Repeat11To138 = 18; + + private const int EofSymbol = 256; + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit length codes. + private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; + + private static readonly short[] StaticLCodes; + private static readonly byte[] StaticLLength; + private static readonly short[] StaticDCodes; + private static readonly byte[] StaticDLength; + + private Tree literalTree; + private Tree distTree; + private Tree blTree; + + // Buffer for distances + private readonly IMemoryOwner distanceManagedBuffer; + private readonly short* pinnedDistanceBuffer; + private MemoryHandle distanceBufferHandle; + + private readonly IMemoryOwner literalManagedBuffer; + private readonly short* pinnedLiteralBuffer; + private MemoryHandle literalBufferHandle; + + private int lastLiteral; + private int extraBits; + private bool isDisposed; + + // TODO: These should be pre-generated array/readonlyspans. + static DeflaterHuffman() + { + // See RFC 1951 3.2.6 + // Literal codes + StaticLCodes = new short[LiteralNumber]; + StaticLLength = new byte[LiteralNumber]; + + int i = 0; + while (i < 144) + { + StaticLCodes[i] = BitReverse((0x030 + i) << 8); + StaticLLength[i++] = 8; + } + + while (i < 256) + { + StaticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); + StaticLLength[i++] = 9; + } + + while (i < 280) + { + StaticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); + StaticLLength[i++] = 7; + } + + while (i < LiteralNumber) + { + StaticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); + StaticLLength[i++] = 8; + } + + // Distance codes + StaticDCodes = new short[DistanceNumber]; + StaticDLength = new byte[DistanceNumber]; + for (i = 0; i < DistanceNumber; i++) + { + StaticDCodes[i] = BitReverse(i << 11); + StaticDLength[i] = 5; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator to use for buffer allocations. + public DeflaterHuffman(MemoryAllocator memoryAllocator) + { + this.Pending = new DeflaterPendingBuffer(memoryAllocator); + + this.literalTree = new Tree(memoryAllocator, LiteralNumber, 257, 15); + this.distTree = new Tree(memoryAllocator, DistanceNumber, 1, 15); + this.blTree = new Tree(memoryAllocator, BitLengthNumber, 4, 7); + + this.distanceManagedBuffer = memoryAllocator.Allocate(BufferSize); + this.distanceBufferHandle = this.distanceManagedBuffer.Memory.Pin(); + this.pinnedDistanceBuffer = (short*)this.distanceBufferHandle.Pointer; + + this.literalManagedBuffer = memoryAllocator.Allocate(BufferSize); + this.literalBufferHandle = this.literalManagedBuffer.Memory.Pin(); + this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer; + } + + /// + /// Gets the pending buffer to use. + /// + public DeflaterPendingBuffer Pending { get; private set; } + + /// + /// Reset internal state + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Reset() + { + this.lastLiteral = 0; + this.extraBits = 0; + this.literalTree.Reset(); + this.distTree.Reset(); + this.blTree.Reset(); + } + + /// + /// Write all trees to pending buffer + /// + /// The number/rank of treecodes to send. + public void SendAllTrees(int blTreeCodes) + { + this.blTree.BuildCodes(); + this.literalTree.BuildCodes(); + this.distTree.BuildCodes(); + this.Pending.WriteBits(this.literalTree.NumCodes - 257, 5); + this.Pending.WriteBits(this.distTree.NumCodes - 1, 5); + this.Pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) + { + this.Pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3); + } + + this.literalTree.WriteTree(this.Pending, this.blTree); + this.distTree.WriteTree(this.Pending, this.blTree); + } + + /// + /// Compress current buffer writing data to pending buffer + /// + public void CompressBlock() + { + DeflaterPendingBuffer pendingBuffer = this.Pending; + short* pinnedDistance = this.pinnedDistanceBuffer; + short* pinnedLiteral = this.pinnedLiteralBuffer; + + for (int i = 0; i < this.lastLiteral; i++) + { + int litlen = pinnedLiteral[i] & 0xFF; + int dist = pinnedDistance[i]; + if (dist-- != 0) + { + int lc = Lcode(litlen); + this.literalTree.WriteSymbol(pendingBuffer, lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) + { + this.Pending.WriteBits(litlen & ((1 << bits) - 1), bits); + } + + int dc = Dcode(dist); + this.distTree.WriteSymbol(pendingBuffer, dc); + + bits = (dc >> 1) - 1; + if (bits > 0) + { + this.Pending.WriteBits(dist & ((1 << bits) - 1), bits); + } + } + else + { + this.literalTree.WriteSymbol(pendingBuffer, litlen); + } + } + + this.literalTree.WriteSymbol(pendingBuffer, EofSymbol); + } + + /// + /// Flush block to output with no compression + /// + /// Data to write + /// Index of first byte to write + /// Count of bytes to write + /// True if this is the last block + [MethodImpl(InliningOptions.ShortMethod)] + public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { + this.Pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3); + this.Pending.AlignToByte(); + this.Pending.WriteShort(storedLength); + this.Pending.WriteShort(~storedLength); + this.Pending.WriteBlock(stored, storedOffset, storedLength); + this.Reset(); + } + + /// + /// Flush block to output with compression + /// + /// Data to flush + /// Index of first byte to flush + /// Count of bytes to flush + /// True if this is the last block + public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { + this.literalTree.Frequencies[EofSymbol]++; + + // Build trees + this.literalTree.BuildTree(); + this.distTree.BuildTree(); + + // Calculate bitlen frequency + this.literalTree.CalcBLFreq(this.blTree); + this.distTree.CalcBLFreq(this.blTree); + + // Build bitlen tree + this.blTree.BuildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) + { + if (this.blTree.Length[BitLengthOrder[i]] > 0) + { + blTreeCodes = i + 1; + } + } + + int opt_len = 14 + (blTreeCodes * 3) + this.blTree.GetEncodedLength() + + this.literalTree.GetEncodedLength() + this.distTree.GetEncodedLength() + + this.extraBits; + + int static_len = this.extraBits; + ref byte staticLLengthRef = ref MemoryMarshal.GetReference(StaticLLength); + for (int i = 0; i < LiteralNumber; i++) + { + static_len += this.literalTree.Frequencies[i] * Unsafe.Add(ref staticLLengthRef, i); + } + + ref byte staticDLengthRef = ref MemoryMarshal.GetReference(StaticDLength); + for (int i = 0; i < DistanceNumber; i++) + { + static_len += this.distTree.Frequencies[i] * Unsafe.Add(ref staticDLengthRef, i); + } + + if (opt_len >= static_len) + { + // Force static trees + opt_len = static_len; + } + + if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) + { + // Store Block + this.FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); + } + else if (opt_len == static_len) + { + // Encode with static tree + this.Pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); + this.literalTree.SetStaticCodes(StaticLCodes, StaticLLength); + this.distTree.SetStaticCodes(StaticDCodes, StaticDLength); + this.CompressBlock(); + this.Reset(); + } + else + { + // Encode with dynamic tree + this.Pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); + this.SendAllTrees(blTreeCodes); + this.CompressBlock(); + this.Reset(); + } + } + + /// + /// Get value indicating if internal buffer is full + /// + /// true if buffer is full + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsFull() => this.lastLiteral >= BufferSize; + + /// + /// Add literal to buffer + /// + /// Literal value to add to buffer. + /// Value indicating internal buffer is full + [MethodImpl(InliningOptions.ShortMethod)] + public bool TallyLit(int literal) + { + this.pinnedDistanceBuffer[this.lastLiteral] = 0; + this.pinnedLiteralBuffer[this.lastLiteral++] = (byte)literal; + this.literalTree.Frequencies[literal]++; + return this.IsFull(); + } + + /// + /// Add distance code and length to literal and distance trees + /// + /// Distance code + /// Length + /// Value indicating if internal buffer is full + [MethodImpl(InliningOptions.ShortMethod)] + public bool TallyDist(int distance, int length) + { + this.pinnedDistanceBuffer[this.lastLiteral] = (short)distance; + this.pinnedLiteralBuffer[this.lastLiteral++] = (byte)(length - 3); + + int lc = Lcode(length - 3); + this.literalTree.Frequencies[lc]++; + if (lc >= 265 && lc < 285) + { + this.extraBits += (lc - 261) / 4; + } + + int dc = Dcode(distance - 1); + this.distTree.Frequencies[dc]++; + if (dc >= 4) + { + this.extraBits += (dc >> 1) - 1; + } + + return this.IsFull(); + } + + /// + /// Reverse the bits of a 16 bit value. + /// + /// Value to reverse bits + /// Value with bits reversed + [MethodImpl(InliningOptions.ShortMethod)] + public static short BitReverse(int toReverse) + { + return (short)(Bit4Reverse[toReverse & 0xF] << 12 + | Bit4Reverse[(toReverse >> 4) & 0xF] << 8 + | Bit4Reverse[(toReverse >> 8) & 0xF] << 4 + | Bit4Reverse[toReverse >> 12]); + } + + /// + public void Dispose() + { + if (!this.isDisposed) + { + this.Pending.Dispose(); + this.distanceBufferHandle.Dispose(); + this.distanceManagedBuffer.Dispose(); + this.literalBufferHandle.Dispose(); + this.literalManagedBuffer.Dispose(); + + this.literalTree.Dispose(); + this.blTree.Dispose(); + this.distTree.Dispose(); + + this.Pending = null; + this.literalTree = null; + this.blTree = null; + this.distTree = null; + this.isDisposed = true; + } + + GC.SuppressFinalize(this); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static int Lcode(int length) + { + if (length == 255) + { + return 285; + } + + int code = 257; + while (length >= 8) + { + code += 4; + length >>= 1; + } + + return code + length; + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static int Dcode(int distance) + { + int code = 0; + while (distance >= 4) + { + code += 2; + distance >>= 1; + } + + return code + distance; + } + + private sealed class Tree : IDisposable + { + private readonly int minNumCodes; + private readonly int[] bitLengthCounts; + private readonly int maxLength; + private bool isDisposed; + + private readonly int elementCount; + + private readonly MemoryAllocator memoryAllocator; + + private IMemoryOwner codesMemoryOwner; + private MemoryHandle codesMemoryHandle; + private readonly short* codes; + + private IMemoryOwner frequenciesMemoryOwner; + private MemoryHandle frequenciesMemoryHandle; + + private IManagedByteBuffer lengthsMemoryOwner; + private MemoryHandle lengthsMemoryHandle; + + public Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int maxLength) + { + this.memoryAllocator = memoryAllocator; + this.elementCount = elements; + this.minNumCodes = minCodes; + this.maxLength = maxLength; + + this.frequenciesMemoryOwner = memoryAllocator.Allocate(elements); + this.frequenciesMemoryHandle = this.frequenciesMemoryOwner.Memory.Pin(); + this.Frequencies = (short*)this.frequenciesMemoryHandle.Pointer; + + this.lengthsMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(elements); + this.lengthsMemoryHandle = this.lengthsMemoryOwner.Memory.Pin(); + this.Length = (byte*)this.lengthsMemoryHandle.Pointer; + + this.codesMemoryOwner = memoryAllocator.Allocate(elements); + this.codesMemoryHandle = this.codesMemoryOwner.Memory.Pin(); + this.codes = (short*)this.codesMemoryHandle.Pointer; + + // Maxes out at 15. + this.bitLengthCounts = new int[maxLength]; + } + + public int NumCodes { get; private set; } + + public short* Frequencies { get; } + + public byte* Length { get; } + + /// + /// Resets the internal state of the tree + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Reset() + { + this.frequenciesMemoryOwner.Memory.Span.Clear(); + this.lengthsMemoryOwner.Memory.Span.Clear(); + this.codesMemoryOwner.Memory.Span.Clear(); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void WriteSymbol(DeflaterPendingBuffer pendingBuffer, int code) + => pendingBuffer.WriteBits(this.codes[code] & 0xFFFF, this.Length[code]); + + /// + /// Set static codes and length + /// + /// new codes + /// length for new codes + [MethodImpl(InliningOptions.ShortMethod)] + public void SetStaticCodes(ReadOnlySpan staticCodes, ReadOnlySpan staticLengths) + { + staticCodes.CopyTo(this.codesMemoryOwner.Memory.Span); + staticLengths.CopyTo(this.lengthsMemoryOwner.Memory.Span); + } + + /// + /// Build dynamic codes and lengths + /// + public void BuildCodes() + { + // Maxes out at 15 * 4 + Span nextCode = stackalloc int[this.maxLength]; + ref int nextCodeRef = ref MemoryMarshal.GetReference(nextCode); + ref int bitLengthCountsRef = ref MemoryMarshal.GetReference(this.bitLengthCounts); + + int code = 0; + for (int bits = 0; bits < this.maxLength; bits++) + { + Unsafe.Add(ref nextCodeRef, bits) = code; + code += Unsafe.Add(ref bitLengthCountsRef, bits) << (15 - bits); + } + + for (int i = 0; i < this.NumCodes; i++) + { + int bits = this.Length[i]; + if (bits > 0) + { + this.codes[i] = BitReverse(Unsafe.Add(ref nextCodeRef, bits - 1)); + Unsafe.Add(ref nextCodeRef, bits - 1) += 1 << (16 - bits); + } + } + } + + public void BuildTree() + { + int numSymbols = this.elementCount; + + // heap is a priority queue, sorted by frequency, least frequent + // nodes first. The heap is a binary tree, with the property, that + // the parent node is smaller than both child nodes. This assures + // that the smallest node is the first parent. + // + // The binary tree is encoded in an array: 0 is root node and + // the nodes 2*n+1, 2*n+2 are the child nodes of node n. + // Maxes out at 286 * 4 so too large for the stack. + using (IMemoryOwner heapMemoryOwner = this.memoryAllocator.Allocate(numSymbols)) + { + ref int heapRef = ref MemoryMarshal.GetReference(heapMemoryOwner.Memory.Span); + + int heapLen = 0; + int maxCode = 0; + for (int n = 0; n < numSymbols; n++) + { + int freq = this.Frequencies[n]; + if (freq != 0) + { + // Insert n into heap + int pos = heapLen++; + int ppos; + while (pos > 0 && this.Frequencies[Unsafe.Add(ref heapRef, ppos = (pos - 1) >> 1)] > freq) + { + Unsafe.Add(ref heapRef, pos) = Unsafe.Add(ref heapRef, ppos); + pos = ppos; + } + + Unsafe.Add(ref heapRef, pos) = n; + + maxCode = n; + } + } + + // We could encode a single literal with 0 bits but then we + // don't see the literals. Therefore we force at least two + // literals to avoid this case. We don't care about order in + // this case, both literals get a 1 bit code. + while (heapLen < 2) + { + Unsafe.Add(ref heapRef, heapLen++) = maxCode < 2 ? ++maxCode : 0; + } + + this.NumCodes = Math.Max(maxCode + 1, this.minNumCodes); + + int numLeafs = heapLen; + int childrenLength = (4 * heapLen) - 2; + using (IMemoryOwner childrenMemoryOwner = this.memoryAllocator.Allocate(childrenLength)) + using (IMemoryOwner valuesMemoryOwner = this.memoryAllocator.Allocate((2 * heapLen) - 1)) + { + ref int childrenRef = ref MemoryMarshal.GetReference(childrenMemoryOwner.Memory.Span); + ref int valuesRef = ref MemoryMarshal.GetReference(valuesMemoryOwner.Memory.Span); + int numNodes = numLeafs; + + for (int i = 0; i < heapLen; i++) + { + int node = Unsafe.Add(ref heapRef, i); + int i2 = 2 * i; + Unsafe.Add(ref childrenRef, i2) = node; + Unsafe.Add(ref childrenRef, i2 + 1) = -1; + Unsafe.Add(ref valuesRef, i) = this.Frequencies[node] << 8; + Unsafe.Add(ref heapRef, i) = i; + } + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + do + { + int first = Unsafe.Add(ref heapRef, 0); + int last = Unsafe.Add(ref heapRef, --heapLen); + + // Propagate the hole to the leafs of the heap + int ppos = 0; + int path = 1; + + while (path < heapLen) + { + if (path + 1 < heapLen && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path)) > Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path + 1))) + { + path++; + } + + Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path); + ppos = path; + path = (path * 2) + 1; + } + + // Now propagate the last element down along path. Normally + // it shouldn't go too deep. + int lastVal = Unsafe.Add(ref valuesRef, last); + while ((path = ppos) > 0 + && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, ppos = (path - 1) >> 1)) > lastVal) + { + Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos); + } + + Unsafe.Add(ref heapRef, path) = last; + + int second = Unsafe.Add(ref heapRef, 0); + + // Create a new node father of first and second + last = numNodes++; + Unsafe.Add(ref childrenRef, 2 * last) = first; + Unsafe.Add(ref childrenRef, (2 * last) + 1) = second; + int mindepth = Math.Min(Unsafe.Add(ref valuesRef, first) & 0xFF, Unsafe.Add(ref valuesRef, second) & 0xFF); + Unsafe.Add(ref valuesRef, last) = lastVal = Unsafe.Add(ref valuesRef, first) + Unsafe.Add(ref valuesRef, second) - mindepth + 1; + + // Again, propagate the hole to the leafs + ppos = 0; + path = 1; + + while (path < heapLen) + { + if (path + 1 < heapLen + && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path)) > Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, path + 1))) + { + path++; + } + + Unsafe.Add(ref heapRef, ppos) = Unsafe.Add(ref heapRef, path); + ppos = path; + path = (ppos * 2) + 1; + } + + // Now propagate the new element down along path + while ((path = ppos) > 0 && Unsafe.Add(ref valuesRef, Unsafe.Add(ref heapRef, ppos = (path - 1) >> 1)) > lastVal) + { + Unsafe.Add(ref heapRef, path) = Unsafe.Add(ref heapRef, ppos); + } + + Unsafe.Add(ref heapRef, path) = last; + } + while (heapLen > 1); + + if (Unsafe.Add(ref heapRef, 0) != (childrenLength >> 1) - 1) + { + DeflateThrowHelper.ThrowHeapViolated(); + } + + this.BuildLength(childrenMemoryOwner.Memory.Span); + } + } + } + + /// + /// Get encoded length + /// + /// Encoded length, the sum of frequencies * lengths + [MethodImpl(InliningOptions.ShortMethod)] + public int GetEncodedLength() + { + int len = 0; + for (int i = 0; i < this.elementCount; i++) + { + len += this.Frequencies[i] * this.Length[i]; + } + + return len; + } + + /// + /// Scan a literal or distance tree to determine the frequencies of the codes + /// in the bit length tree. + /// + public void CalcBLFreq(Tree blTree) + { + int maxCount; // max repeat count + int minCount; // min repeat count + int count; // repeat count of the current code + int curLen = -1; // length of current code + + int i = 0; + while (i < this.NumCodes) + { + count = 1; + int nextlen = this.Length[i]; + if (nextlen == 0) + { + maxCount = 138; + minCount = 3; + } + else + { + maxCount = 6; + minCount = 3; + if (curLen != nextlen) + { + blTree.Frequencies[nextlen]++; + count = 0; + } + } + + curLen = nextlen; + i++; + + while (i < this.NumCodes && curLen == this.Length[i]) + { + i++; + if (++count >= maxCount) + { + break; + } + } + + if (count < minCount) + { + blTree.Frequencies[curLen] += (short)count; + } + else if (curLen != 0) + { + blTree.Frequencies[Repeat3To6]++; + } + else if (count <= 10) + { + blTree.Frequencies[Repeat3To10]++; + } + else + { + blTree.Frequencies[Repeat11To138]++; + } + } + } + + /// + /// Write the tree values. + /// + /// The pending buffer. + /// The tree to write. + public void WriteTree(DeflaterPendingBuffer pendingBuffer, Tree bitLengthTree) + { + int maxCount; // max repeat count + int minCount; // min repeat count + int count; // repeat count of the current code + int curLen = -1; // length of current code + + int i = 0; + while (i < this.NumCodes) + { + count = 1; + int nextlen = this.Length[i]; + if (nextlen == 0) + { + maxCount = 138; + minCount = 3; + } + else + { + maxCount = 6; + minCount = 3; + if (curLen != nextlen) + { + bitLengthTree.WriteSymbol(pendingBuffer, nextlen); + count = 0; + } + } + + curLen = nextlen; + i++; + + while (i < this.NumCodes && curLen == this.Length[i]) + { + i++; + if (++count >= maxCount) + { + break; + } + } + + if (count < minCount) + { + while (count-- > 0) + { + bitLengthTree.WriteSymbol(pendingBuffer, curLen); + } + } + else if (curLen != 0) + { + bitLengthTree.WriteSymbol(pendingBuffer, Repeat3To6); + pendingBuffer.WriteBits(count - 3, 2); + } + else if (count <= 10) + { + bitLengthTree.WriteSymbol(pendingBuffer, Repeat3To10); + pendingBuffer.WriteBits(count - 3, 3); + } + else + { + bitLengthTree.WriteSymbol(pendingBuffer, Repeat11To138); + pendingBuffer.WriteBits(count - 11, 7); + } + } + } + + private void BuildLength(ReadOnlySpan children) + { + byte* lengthPtr = this.Length; + ref int childrenRef = ref MemoryMarshal.GetReference(children); + ref int bitLengthCountsRef = ref MemoryMarshal.GetReference(this.bitLengthCounts); + + int maxLen = this.maxLength; + int numNodes = children.Length >> 1; + int numLeafs = (numNodes + 1) >> 1; + int overflow = 0; + + Array.Clear(this.bitLengthCounts, 0, maxLen); + + // First calculate optimal bit lengths + using (IMemoryOwner lengthsMemoryOwner = this.memoryAllocator.Allocate(numNodes, AllocationOptions.Clean)) + { + ref int lengthsRef = ref MemoryMarshal.GetReference(lengthsMemoryOwner.Memory.Span); + + for (int i = numNodes - 1; i >= 0; i--) + { + if (children[(2 * i) + 1] != -1) + { + int bitLength = Unsafe.Add(ref lengthsRef, i) + 1; + if (bitLength > maxLen) + { + bitLength = maxLen; + overflow++; + } + + Unsafe.Add(ref lengthsRef, Unsafe.Add(ref childrenRef, 2 * i)) = Unsafe.Add(ref lengthsRef, Unsafe.Add(ref childrenRef, (2 * i) + 1)) = bitLength; + } + else + { + // A leaf node + int bitLength = Unsafe.Add(ref lengthsRef, i); + Unsafe.Add(ref bitLengthCountsRef, bitLength - 1)++; + lengthPtr[Unsafe.Add(ref childrenRef, 2 * i)] = (byte)Unsafe.Add(ref lengthsRef, i); + } + } + } + + if (overflow == 0) + { + return; + } + + int incrBitLen = maxLen - 1; + do + { + // Find the first bit length which could increase: + while (Unsafe.Add(ref bitLengthCountsRef, --incrBitLen) == 0) + { + } + + // Move this node one down and remove a corresponding + // number of overflow nodes. + do + { + Unsafe.Add(ref bitLengthCountsRef, incrBitLen)--; + Unsafe.Add(ref bitLengthCountsRef, ++incrBitLen)++; + overflow -= 1 << (maxLen - 1 - incrBitLen); + } + while (overflow > 0 && incrBitLen < maxLen - 1); + } + while (overflow > 0); + + // We may have overshot above. Move some nodes from maxLength to + // maxLength-1 in that case. + Unsafe.Add(ref bitLengthCountsRef, maxLen - 1) += overflow; + Unsafe.Add(ref bitLengthCountsRef, maxLen - 2) -= overflow; + + // Now recompute all bit lengths, scanning in increasing + // frequency. It is simpler to reconstruct all lengths instead of + // fixing only the wrong ones. This idea is taken from 'ar' + // written by Haruhiko Okumura. + // + // The nodes were inserted with decreasing frequency into the childs + // array. + int nodeIndex = 2 * numLeafs; + for (int bits = maxLen; bits != 0; bits--) + { + int n = Unsafe.Add(ref bitLengthCountsRef, bits - 1); + while (n > 0) + { + int childIndex = 2 * Unsafe.Add(ref childrenRef, nodeIndex++); + if (Unsafe.Add(ref childrenRef, childIndex + 1) == -1) + { + // We found another leaf + lengthPtr[Unsafe.Add(ref childrenRef, childIndex)] = (byte)bits; + n--; + } + } + } + } + + public void Dispose() + { + if (!this.isDisposed) + { + this.frequenciesMemoryHandle.Dispose(); + this.frequenciesMemoryOwner.Dispose(); + + this.lengthsMemoryHandle.Dispose(); + this.lengthsMemoryOwner.Dispose(); + + this.codesMemoryHandle.Dispose(); + this.codesMemoryOwner.Dispose(); + + this.frequenciesMemoryOwner = null; + this.lengthsMemoryOwner = null; + this.codesMemoryOwner = null; + + this.isDisposed = true; + } + + GC.SuppressFinalize(this); + } + } + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs new file mode 100644 index 0000000000..a777e6f7db --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterOutputStream.cs @@ -0,0 +1,153 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ + /// + /// A special stream deflating or compressing the bytes that are + /// written to it. It uses a Deflater to perform actual deflating. + /// + internal sealed class DeflaterOutputStream : Stream + { + private const int BufferLength = 512; + private IManagedByteBuffer memoryOwner; + private readonly byte[] buffer; + private Deflater deflater; + private readonly Stream rawStream; + private bool isDisposed; + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator to use for buffer allocations. + /// The output stream where deflated output is written. + /// The compression level. + public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, int compressionLevel) + { + this.rawStream = rawStream; + this.memoryOwner = memoryAllocator.AllocateManagedByteBuffer(BufferLength); + this.buffer = this.memoryOwner.Array; + this.deflater = new Deflater(memoryAllocator, compressionLevel); + } + + /// + public override bool CanRead => false; + + /// + public override bool CanSeek => false; + + /// + public override bool CanWrite => this.rawStream.CanWrite; + + /// + public override long Length => this.rawStream.Length; + + /// + public override long Position + { + get + { + return this.rawStream.Position; + } + + set + { + throw new NotSupportedException(); + } + } + + /// + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + + /// + public override void SetLength(long value) => throw new NotSupportedException(); + + /// + public override int ReadByte() => throw new NotSupportedException(); + + /// + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + /// + public override void Flush() + { + this.deflater.Flush(); + this.Deflate(true); + this.rawStream.Flush(); + } + + /// + public override void Write(byte[] buffer, int offset, int count) + { + this.deflater.SetInput(buffer, offset, count); + this.Deflate(); + } + + private void Deflate() => this.Deflate(false); + + private void Deflate(bool flushing) + { + while (flushing || !this.deflater.IsNeedingInput) + { + int deflateCount = this.deflater.Deflate(this.buffer, 0, BufferLength); + + if (deflateCount <= 0) + { + break; + } + + this.rawStream.Write(this.buffer, 0, deflateCount); + } + + if (!this.deflater.IsNeedingInput) + { + DeflateThrowHelper.ThrowNoDeflate(); + } + } + + private void Finish() + { + this.deflater.Finish(); + while (!this.deflater.IsFinished) + { + int len = this.deflater.Deflate(this.buffer, 0, BufferLength); + if (len <= 0) + { + break; + } + + this.rawStream.Write(this.buffer, 0, len); + } + + if (!this.deflater.IsFinished) + { + DeflateThrowHelper.ThrowNoDeflate(); + } + + this.rawStream.Flush(); + } + + /// + protected override void Dispose(bool disposing) + { + if (!this.isDisposed) + { + if (disposing) + { + this.Finish(); + this.deflater.Dispose(); + this.memoryOwner.Dispose(); + } + + this.deflater = null; + this.memoryOwner = null; + this.isDisposed = true; + base.Dispose(disposing); + } + } + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs new file mode 100644 index 0000000000..731c9e80f0 --- /dev/null +++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs @@ -0,0 +1,179 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ + /// + /// Stores pending data for writing data to the Deflater. + /// + internal sealed unsafe class DeflaterPendingBuffer : IDisposable + { + private readonly byte[] buffer; + private readonly byte* pinnedBuffer; + private IManagedByteBuffer bufferMemoryOwner; + private MemoryHandle bufferMemoryHandle; + + private int start; + private int end; + private uint bits; + private bool isDisposed; + + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator to use for buffer allocations. + public DeflaterPendingBuffer(MemoryAllocator memoryAllocator) + { + this.bufferMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(DeflaterConstants.PENDING_BUF_SIZE); + this.buffer = this.bufferMemoryOwner.Array; + this.bufferMemoryHandle = this.bufferMemoryOwner.Memory.Pin(); + this.pinnedBuffer = (byte*)this.bufferMemoryHandle.Pointer; + } + + /// + /// Gets the number of bits written to the buffer. + /// + public int BitCount { get; private set; } + + /// + /// Gets a value indicating whether indicates the buffer has been flushed. + /// + public bool IsFlushed => this.end == 0; + + /// + /// Clear internal state/buffers. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Reset() => this.start = this.end = this.BitCount = 0; + + /// + /// Write a short value to buffer LSB first. + /// + /// The value to write. + [MethodImpl(InliningOptions.ShortMethod)] + public void WriteShort(int value) + { + byte* pinned = this.pinnedBuffer; + pinned[this.end++] = unchecked((byte)value); + pinned[this.end++] = unchecked((byte)(value >> 8)); + } + + /// + /// Write a block of data to the internal buffer. + /// + /// The data to write. + /// The offset of first byte to write. + /// The number of bytes to write. + [MethodImpl(InliningOptions.ShortMethod)] + public void WriteBlock(byte[] block, int offset, int length) + { + Unsafe.CopyBlockUnaligned(ref this.buffer[this.end], ref block[offset], unchecked((uint)length)); + this.end += length; + } + + /// + /// Aligns internal buffer on a byte boundary. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void AlignToByte() + { + if (this.BitCount > 0) + { + byte* pinned = this.pinnedBuffer; + pinned[this.end++] = unchecked((byte)this.bits); + if (this.BitCount > 8) + { + pinned[this.end++] = unchecked((byte)(this.bits >> 8)); + } + } + + this.bits = 0; + this.BitCount = 0; + } + + /// + /// Write bits to internal buffer + /// + /// source of bits + /// number of bits to write + [MethodImpl(InliningOptions.ShortMethod)] + public void WriteBits(int b, int count) + { + this.bits |= (uint)(b << this.BitCount); + this.BitCount += count; + if (this.BitCount >= 16) + { + byte* pinned = this.pinnedBuffer; + pinned[this.end++] = unchecked((byte)this.bits); + pinned[this.end++] = unchecked((byte)(this.bits >> 8)); + this.bits >>= 16; + this.BitCount -= 16; + } + } + + /// + /// Write a short value to internal buffer most significant byte first + /// + /// The value to write + [MethodImpl(InliningOptions.ShortMethod)] + public void WriteShortMSB(int value) + { + byte* pinned = this.pinnedBuffer; + pinned[this.end++] = unchecked((byte)(value >> 8)); + pinned[this.end++] = unchecked((byte)value); + } + + /// + /// Flushes the pending buffer into the given output array. + /// If the output array is to small, only a partial flush is done. + /// + /// The output array. + /// The offset into output array. + /// The maximum number of bytes to store. + /// The number of bytes flushed. + public int Flush(byte[] output, int offset, int length) + { + if (this.BitCount >= 8) + { + this.pinnedBuffer[this.end++] = unchecked((byte)this.bits); + this.bits >>= 8; + this.BitCount -= 8; + } + + if (length > this.end - this.start) + { + length = this.end - this.start; + + Unsafe.CopyBlockUnaligned(ref output[offset], ref this.buffer[this.start], unchecked((uint)length)); + this.start = 0; + this.end = 0; + } + else + { + Unsafe.CopyBlockUnaligned(ref output[offset], ref this.buffer[this.start], unchecked((uint)length)); + this.start += length; + } + + return length; + } + + /// + public void Dispose() + { + if (!this.isDisposed) + { + this.bufferMemoryHandle.Dispose(); + this.bufferMemoryOwner.Dispose(); + this.bufferMemoryOwner = null; + this.isDisposed = true; + } + + GC.SuppressFinalize(this); + } + } +} diff --git a/src/ImageSharp/Formats/Png/Zlib/README.md b/src/ImageSharp/Formats/Png/Zlib/README.md index c297a91d5e..59f75d05f6 100644 --- a/src/ImageSharp/Formats/Png/Zlib/README.md +++ b/src/ImageSharp/Formats/Png/Zlib/README.md @@ -1,2 +1,5 @@ -Adler32.cs and Crc32.cs have been copied from -https://github.com/ygrenier/SharpZipLib.Portable +Deflatestream implementation adapted from + +https://github.com/icsharpcode/SharpZipLib + +LIcensed under MIT diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index 8e0bac938f..c723b463f8 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.IO; -using System.IO.Compression; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Png.Zlib { @@ -38,14 +38,16 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// /// The stream responsible for compressing the input stream. /// - private System.IO.Compression.DeflateStream deflateStream; + // private DeflateStream deflateStream; + private DeflaterOutputStream deflateStream; /// /// Initializes a new instance of the class. /// + /// The memory allocator to use for buffer allocations. /// The stream to compress. /// The compression level. - public ZlibDeflateStream(Stream stream, int compressionLevel) + public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, int compressionLevel) { this.rawStream = stream; @@ -60,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib // +---+---+ // |CMF|FLG| // +---+---+ - int cmf = 0x78; + const int Cmf = 0x78; int flg = 218; // http://stackoverflow.com/a/2331025/277304 @@ -78,29 +80,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } // Just in case - flg -= ((cmf * 256) + flg) % 31; + flg -= ((Cmf * 256) + flg) % 31; if (flg < 0) { flg += 31; } - this.rawStream.WriteByte((byte)cmf); + this.rawStream.WriteByte(Cmf); this.rawStream.WriteByte((byte)flg); - // Initialize the deflate Stream. - CompressionLevel level = CompressionLevel.Optimal; - - if (compressionLevel >= 1 && compressionLevel <= 5) - { - level = CompressionLevel.Fastest; - } - else if (compressionLevel == 0) - { - level = CompressionLevel.NoCompression; - } - - this.deflateStream = new System.IO.Compression.DeflateStream(this.rawStream, level, true); + this.deflateStream = new DeflaterOutputStream(memoryAllocator, this.rawStream, compressionLevel); } /// @@ -110,41 +100,36 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public override bool CanSeek => false; /// - public override bool CanWrite => true; + public override bool CanWrite => this.rawStream.CanWrite; /// - public override long Length => throw new NotSupportedException(); + public override long Length => this.rawStream.Length; /// public override long Position { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); + get + { + return this.rawStream.Position; + } + + set + { + throw new NotSupportedException(); + } } /// - public override void Flush() - { - this.deflateStream?.Flush(); - } + public override void Flush() => this.deflateStream.Flush(); /// - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); /// - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); /// - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) => throw new NotSupportedException(); /// public override void Write(byte[] buffer, int offset, int count) @@ -164,17 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (disposing) { // dispose managed resources - if (this.deflateStream != null) - { - this.deflateStream.Dispose(); - this.deflateStream = null; - } - else - { - // Hack: empty input? - this.rawStream.WriteByte(3); - this.rawStream.WriteByte(0); - } + this.deflateStream.Dispose(); // Add the crc uint crc = (uint)this.adler32.Value; @@ -184,11 +159,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.rawStream.WriteByte((byte)(crc & 0xFF)); } - base.Dispose(disposing); + this.deflateStream = null; - // Call the appropriate methods to clean up - // unmanaged resources here. - // Note disposing is done. + base.Dispose(disposing); this.isDisposed = true; } } diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index e4645c44ac..3eb34b8617 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib internal sealed class ZlibInflateStream : Stream { /// - /// Used to read the Adler-32 and Crc-32 checksums + /// 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. /// @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib private static readonly Func GetDataNoOp = () => 0; /// - /// The inner raw memory stream + /// The inner raw memory stream. /// private readonly Stream innerStream; @@ -43,12 +43,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib private bool isDisposed; /// - /// The current data remaining to be read + /// The current data remaining to be read. /// private int currentDataRemaining; /// - /// Delegate to get more data once we've exhausted the current data remaining + /// Delegate to get more data once we've exhausted the current data remaining. /// private readonly Func getData; @@ -88,14 +88,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } /// - /// Gets the compressed stream over the deframed inner stream + /// Gets the compressed stream over the deframed inner stream. /// public DeflateStream CompressedStream { get; private set; } /// - /// Adds new bytes from a frame found in the original stream + /// Adds new bytes from a frame found in the original stream. /// - /// blabla + /// The current remaining data according to the chunk length. /// Whether the chunk to be inflated is a critical chunk. /// The . public bool AllocateNewBytes(int bytes, bool isCriticalChunk) @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { if (this.currentDataRemaining == 0) { - // last buffer was read in its entirety, let's make sure we don't actually have more + // Last buffer was read in its entirety, let's make sure we don't actually have more in additional IDAT chunks. this.currentDataRemaining = this.getData(); if (this.currentDataRemaining == 0) @@ -135,32 +135,35 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib int bytesToRead = Math.Min(count, this.currentDataRemaining); this.currentDataRemaining -= bytesToRead; - int bytesRead = this.innerStream.Read(buffer, offset, bytesToRead); - long length = this.innerStream.Length; + int totalBytesRead = this.innerStream.Read(buffer, offset, bytesToRead); + long innerStreamLength = this.innerStream.Length; - // Keep reading data until we've reached the end of the stream or filled the buffer - while (this.currentDataRemaining == 0 && bytesRead < count) + // Keep reading data until we've reached the end of the stream or filled the buffer. + int bytesRead = 0; + offset += totalBytesRead; + while (this.currentDataRemaining == 0 && totalBytesRead < count) { this.currentDataRemaining = this.getData(); if (this.currentDataRemaining == 0) { - return bytesRead; + return totalBytesRead; } offset += bytesRead; - if (offset >= length || offset >= count) + if (offset >= innerStreamLength || offset >= count) { - return bytesRead; + return totalBytesRead; } - bytesToRead = Math.Min(count - bytesRead, this.currentDataRemaining); + bytesToRead = Math.Min(count - totalBytesRead, this.currentDataRemaining); this.currentDataRemaining -= bytesToRead; - bytesRead += this.innerStream.Read(buffer, offset, bytesToRead); + bytesRead = this.innerStream.Read(buffer, offset, bytesToRead); + totalBytesRead += bytesRead; } - return bytesRead; + return totalBytesRead; } /// @@ -191,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (disposing) { - // dispose managed resources + // Dispose managed resources. if (this.CompressedStream != null) { this.CompressedStream.Dispose(); diff --git a/src/ImageSharp/Formats/README.md b/src/ImageSharp/Formats/README.md new file mode 100644 index 0000000000..4a2b401b1d --- /dev/null +++ b/src/ImageSharp/Formats/README.md @@ -0,0 +1,6 @@ +# Encoder/Decoder for true vision targa files + +Useful links for reference: + +- [FileFront](https://www.fileformat.info/format/tga/egff.htm) +- [Tga Specification](http://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf) diff --git a/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs new file mode 100644 index 0000000000..e99e8b0c8d --- /dev/null +++ b/src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs @@ -0,0 +1,12 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// The options for decoding tga images. Currently empty, but this may change in the future. + /// + internal interface ITgaDecoderOptions + { + } +} diff --git a/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs b/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs new file mode 100644 index 0000000000..49983d2369 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/ITgaEncoderOptions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Configuration options for use during tga encoding. + /// + internal interface ITgaEncoderOptions + { + /// + /// Gets the number of bits per pixel. + /// + TgaBitsPerPixel? BitsPerPixel { get; } + + /// + /// Gets a value indicating whether run length compression should be used. + /// + TgaCompression Compression { get; } + } +} diff --git a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs new file mode 100644 index 0000000000..2b0e405e75 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class MetadataExtensions + { + /// + /// Gets the tga format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TgaFormat.Instance); + } +} diff --git a/src/ImageSharp/Formats/Tga/TGA_Specification.pdf b/src/ImageSharp/Formats/Tga/TGA_Specification.pdf new file mode 100644 index 0000000000..09c9a4ddda Binary files /dev/null and b/src/ImageSharp/Formats/Tga/TGA_Specification.pdf differ diff --git a/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs b/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs new file mode 100644 index 0000000000..a0666fa84d --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaBitsPerPixel.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Enumerates the available bits per pixel the tga encoder supports. + /// + public enum TgaBitsPerPixel : byte + { + /// + /// 8 bits per pixel. Each pixel consists of 1 byte. + /// + Pixel8 = 8, + + /// + /// 16 bits per pixel. Each pixel consists of 2 bytes. + /// + Pixel16 = 16, + + /// + /// 24 bits per pixel. Each pixel consists of 3 bytes. + /// + Pixel24 = 24, + + /// + /// 32 bits per pixel. Each pixel consists of 4 bytes. + /// + Pixel32 = 32 + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaCompression.cs b/src/ImageSharp/Formats/Tga/TgaCompression.cs new file mode 100644 index 0000000000..cc6e005eda --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaCompression.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Indicates if compression is used. + /// + public enum TgaCompression + { + /// + /// No compression is used. + /// + None, + + /// + /// Run length encoding is used. + /// + RunLength, + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs new file mode 100644 index 0000000000..18fbf4acd0 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaConfigurationModule.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Registers the image encoders, decoders and mime type detectors for the tga format. + /// + public sealed class TgaConfigurationModule : IConfigurationModule + { + /// + public void Configure(Configuration configuration) + { + configuration.ImageFormatsManager.SetEncoder(TgaFormat.Instance, new TgaEncoder()); + configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, new TgaDecoder()); + configuration.ImageFormatsManager.AddImageFormatDetector(new TgaImageFormatDetector()); + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaConstants.cs b/src/ImageSharp/Formats/Tga/TgaConstants.cs new file mode 100644 index 0000000000..5aabe92a1d --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaConstants.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + internal static class TgaConstants + { + /// + /// The list of mimetypes that equate to a targa file. + /// + public static readonly IEnumerable MimeTypes = new[] { "image/x-tga", "image/x-targa" }; + + /// + /// The list of file extensions that equate to a targa file. + /// + public static readonly IEnumerable FileExtensions = new[] { "tga", "vda", "icb", "vst" }; + + /// + /// The file header length of a tga image in bytes. + /// + public const int FileHeaderLength = 18; + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaDecoder.cs b/src/ImageSharp/Formats/Tga/TgaDecoder.cs new file mode 100644 index 0000000000..b97388773a --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaDecoder.cs @@ -0,0 +1,34 @@ +// 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.Tga +{ + /// + /// Image decoder for Truevision TGA images. + /// + public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector + { + /// + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + return new TgaDecoderCore(configuration, this).Decode(stream); + } + + /// + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + + /// + public IImageInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + return new TgaDecoderCore(configuration, this).Identify(stream); + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs new file mode 100644 index 0000000000..91cc93e19d --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -0,0 +1,584 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + internal sealed class TgaDecoderCore + { + /// + /// The metadata. + /// + private ImageMetadata metadata; + + /// + /// The tga specific metadata. + /// + private TgaMetadata tgaMetadata; + + /// + /// The file header containing general information about the image. + /// + private TgaFileHeader fileHeader; + + /// + /// The global configuration. + /// + private readonly Configuration configuration; + + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + + /// + /// The stream to decode from. + /// + private Stream currentStream; + + /// + /// The bitmap decoder options. + /// + private readonly ITgaDecoderOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) + { + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.options = options; + } + + /// + /// Decodes the image from the specified stream. + /// + /// The pixel format. + /// The stream, where the image should be decoded from. Cannot be null. + /// + /// is null. + /// + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : struct, IPixel + { + try + { + bool inverted = this.ReadFileHeader(stream); + this.currentStream.Skip(this.fileHeader.IdLength); + + // Parse the color map, if present. + if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1) + { + TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found"); + } + + if (this.fileHeader.Width == 0 || this.fileHeader.Height == 0) + { + throw new UnknownImageFormatException("Width or height cannot be 0"); + } + + var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); + Buffer2D pixels = image.GetRootFramePixelBuffer(); + + if (this.fileHeader.ColorMapType is 1) + { + if (this.fileHeader.CMapLength <= 0) + { + TgaThrowHelper.ThrowImageFormatException("Missing tga color map length"); + } + + if (this.fileHeader.CMapDepth <= 0) + { + TgaThrowHelper.ThrowImageFormatException("Missing tga color map depth"); + } + + int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8; + int colorMapSizeInBytes = this.fileHeader.CMapLength * colorMapPixelSizeInBytes; + using (IManagedByteBuffer palette = this.memoryAllocator.AllocateManagedByteBuffer(colorMapSizeInBytes, AllocationOptions.Clean)) + { + this.currentStream.Read(palette.Array, this.fileHeader.CMapStart, colorMapSizeInBytes); + + if (this.fileHeader.ImageType is TgaImageType.RleColorMapped) + { + this.ReadPalettedRle( + this.fileHeader.Width, + this.fileHeader.Height, + pixels, + palette.Array, + colorMapPixelSizeInBytes, + inverted); + } + else + { + this.ReadPaletted( + this.fileHeader.Width, + this.fileHeader.Height, + pixels, + palette.Array, + colorMapPixelSizeInBytes, + inverted); + } + } + + return image; + } + + // Even if the image type indicates it is not a paletted image, it can still contain a palette. Skip those bytes. + if (this.fileHeader.CMapLength > 0) + { + int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8; + this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes); + } + + switch (this.fileHeader.PixelDepth) + { + case 8: + if (this.fileHeader.ImageType.IsRunLengthEncoded()) + { + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, inverted); + } + else + { + this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + } + + break; + + case 15: + case 16: + if (this.fileHeader.ImageType.IsRunLengthEncoded()) + { + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, inverted); + } + else + { + this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + } + + break; + + case 24: + if (this.fileHeader.ImageType.IsRunLengthEncoded()) + { + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, inverted); + } + else + { + this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + } + + break; + + case 32: + if (this.fileHeader.ImageType.IsRunLengthEncoded()) + { + this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, inverted); + } + else + { + this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted); + } + + break; + + default: + TgaThrowHelper.ThrowNotSupportedException("Does not support this kind of tga files."); + break; + } + + return image; + } + catch (IndexOutOfRangeException e) + { + throw new ImageFormatException("TGA image does not have a valid format.", e); + } + } + + /// + /// Reads a uncompressed TGA image with a palette. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// The color palette. + /// Color map size of one entry in bytes. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadPaletted(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, bool inverted) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(width, AllocationOptions.Clean)) + { + TPixel color = default; + Span rowSpan = row.GetSpan(); + + for (int y = 0; y < height; y++) + { + this.currentStream.Read(row); + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.GetRowSpan(newY); + switch (colorMapPixelSizeInBytes) + { + case 2: + for (int x = 0; x < width; x++) + { + int colorIndex = rowSpan[x]; + + // Set alpha value to 1, to treat it as opaque for Bgra5551. + Bgra5551 bgra = Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes]); + bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + color.FromBgra5551(bgra); + pixelRow[x] = color; + } + + break; + + case 3: + for (int x = 0; x < width; x++) + { + int colorIndex = rowSpan[x]; + color.FromBgr24(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + pixelRow[x] = color; + } + + break; + + case 4: + for (int x = 0; x < width; x++) + { + int colorIndex = rowSpan[x]; + color.FromBgra32(Unsafe.As(ref palette[colorIndex * colorMapPixelSizeInBytes])); + pixelRow[x] = color; + } + + break; + } + } + } + } + + /// + /// Reads a run length encoded TGA image with a palette. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// The color palette. + /// Color map size of one entry in bytes. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadPalettedRle(int width, int height, Buffer2D pixels, byte[] palette, int colorMapPixelSizeInBytes, bool inverted) + where TPixel : struct, IPixel + { + int bytesPerPixel = 1; + using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) + { + TPixel color = default; + Span bufferSpan = buffer.GetSpan(); + this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1); + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.GetRowSpan(newY); + int rowStartIdx = y * width * bytesPerPixel; + for (int x = 0; x < width; x++) + { + int idx = rowStartIdx + x; + switch (colorMapPixelSizeInBytes) + { + case 1: + color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + break; + case 2: + // Set alpha value to 1, to treat it as opaque for Bgra5551. + Bgra5551 bgra = Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]); + bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000); + color.FromBgra5551(bgra); + break; + case 3: + color.FromBgr24(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + break; + case 4: + color.FromBgra32(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes])); + break; + } + + pixelRow[x] = color; + } + } + } + } + + /// + /// Reads a uncompressed monochrome TGA image. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadMonoChrome(int width, int height, Buffer2D pixels, bool inverted) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0)) + { + for (int y = 0; y < height; y++) + { + this.currentStream.Read(row); + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + PixelOperations.Instance.FromL8Bytes( + this.configuration, + row.GetSpan(), + pixelSpan, + width); + } + } + } + + /// + /// Reads a uncompressed TGA image where each pixels has 16 bit. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadBgra16(int width, int height, Buffer2D pixels, bool inverted) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0)) + { + for (int y = 0; y < height; y++) + { + this.currentStream.Read(row); + Span rowSpan = row.GetSpan(); + + // We need to set each alpha component value to fully opaque. + for (int x = 1; x < rowSpan.Length; x += 2) + { + rowSpan[x] = (byte)(rowSpan[x] | (1 << 7)); + } + + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + PixelOperations.Instance.FromBgra5551Bytes( + this.configuration, + rowSpan, + pixelSpan, + width); + } + } + } + + /// + /// Reads a uncompressed TGA image where each pixels has 24 bit. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadBgr24(int width, int height, Buffer2D pixels, bool inverted) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0)) + { + for (int y = 0; y < height; y++) + { + this.currentStream.Read(row); + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + PixelOperations.Instance.FromBgr24Bytes( + this.configuration, + row.GetSpan(), + pixelSpan, + width); + } + } + } + + /// + /// Reads a uncompressed TGA image where each pixels has 32 bit. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadBgra32(int width, int height, Buffer2D pixels, bool inverted) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) + { + for (int y = 0; y < height; y++) + { + this.currentStream.Read(row); + int newY = Invert(y, height, inverted); + Span pixelSpan = pixels.GetRowSpan(newY); + PixelOperations.Instance.FromBgra32Bytes( + this.configuration, + row.GetSpan(), + pixelSpan, + width); + } + } + } + + /// + /// Reads a run length encoded TGA image. + /// + /// The pixel type. + /// The width of the image. + /// The height of the image. + /// The to assign the palette to. + /// The bytes per pixel. + /// Indicates, if the origin of the image is top left rather the bottom left (the default). + private void ReadRle(int width, int height, Buffer2D pixels, int bytesPerPixel, bool inverted) + where TPixel : struct, IPixel + { + TPixel color = default; + using (IMemoryOwner buffer = this.memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean)) + { + Span bufferSpan = buffer.GetSpan(); + this.UncompressRle(width, height, bufferSpan, bytesPerPixel); + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.GetRowSpan(newY); + int rowStartIdx = y * width * bytesPerPixel; + for (int x = 0; x < width; x++) + { + int idx = rowStartIdx + (x * bytesPerPixel); + switch (bytesPerPixel) + { + case 1: + color.FromL8(Unsafe.As(ref bufferSpan[idx])); + break; + case 2: + // Set alpha value to 1, to treat it as opaque for Bgra5551. + bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); + color.FromBgra5551(Unsafe.As(ref bufferSpan[idx])); + break; + case 3: + color.FromBgr24(Unsafe.As(ref bufferSpan[idx])); + break; + case 4: + color.FromBgra32(Unsafe.As(ref bufferSpan[idx])); + break; + } + + pixelRow[x] = color; + } + } + } + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public IImageInfo Identify(Stream stream) + { + this.ReadFileHeader(stream); + return new ImageInfo( + new PixelTypeInfo(this.fileHeader.PixelDepth), + this.fileHeader.Width, + this.fileHeader.Height, + this.metadata); + } + + /// + /// Produce uncompressed tga data from a run length encoded stream. + /// + /// The width of the image. + /// The height of the image. + /// Buffer for uncompressed data. + /// The bytes used per pixel. + private void UncompressRle(int width, int height, Span buffer, int bytesPerPixel) + { + int uncompressedPixels = 0; + var pixel = new byte[bytesPerPixel]; + int totalPixels = width * height; + while (uncompressedPixels < totalPixels) + { + byte runLengthByte = (byte)this.currentStream.ReadByte(); + + // The high bit of a run length packet is set to 1. + int highBit = runLengthByte >> 7; + if (highBit == 1) + { + int runLength = runLengthByte & 127; + this.currentStream.Read(pixel, 0, bytesPerPixel); + int bufferIdx = uncompressedPixels * bytesPerPixel; + for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) + { + pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + bufferIdx += bytesPerPixel; + } + } + else + { + // Non-run-length encoded packet. + int runLength = runLengthByte; + int bufferIdx = uncompressedPixels * bytesPerPixel; + for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) + { + this.currentStream.Read(pixel, 0, bytesPerPixel); + pixel.AsSpan().CopyTo(buffer.Slice(bufferIdx)); + bufferIdx += bytesPerPixel; + } + } + } + } + + /// + /// Returns the y- value based on the given height. + /// + /// The y- value representing the current row. + /// 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) => (!inverted) ? height - y - 1 : y; + + /// + /// Reads the tga file header from the stream. + /// + /// The containing image data. + /// true, if the image origin is top left. + private bool ReadFileHeader(Stream stream) + { + this.currentStream = stream; + + Span buffer = stackalloc byte[TgaFileHeader.Size]; + + this.currentStream.Read(buffer, 0, TgaFileHeader.Size); + this.fileHeader = TgaFileHeader.Parse(buffer); + this.metadata = new ImageMetadata(); + this.tgaMetadata = this.metadata.GetTgaMetadata(); + this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; + + // Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom right. + if ((this.fileHeader.ImageDescriptor & (1 << 5)) != 0) + { + return true; + } + + return false; + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaEncoder.cs b/src/ImageSharp/Formats/Tga/TgaEncoder.cs new file mode 100644 index 0000000000..2fcbb822f5 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaEncoder.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Image encoder for writing an image to a stream as a targa truevision image. + /// + public sealed class TgaEncoder : IImageEncoder, ITgaEncoderOptions + { + /// + /// Gets or sets the number of bits per pixel. + /// + public TgaBitsPerPixel? BitsPerPixel { get; set; } + + /// + /// Gets or sets a value indicating whether no compression or run length compression should be used. + /// + public TgaCompression Compression { get; set; } = TgaCompression.RunLength; + + /// + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel + { + var encoder = new TgaEncoderCore(this, image.GetMemoryAllocator()); + encoder.Encode(image, stream); + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs new file mode 100644 index 0000000000..a4b141f389 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs @@ -0,0 +1,342 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers.Binary; +using System.IO; +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Image encoder for writing an image to a stream as a truevision targa image. + /// + internal sealed class TgaEncoderCore + { + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + + /// + /// The global configuration. + /// + private Configuration configuration; + + /// + /// Reusable buffer for writing data. + /// + private readonly byte[] buffer = new byte[2]; + + /// + /// The color depth, in number of bits per pixel. + /// + private TgaBitsPerPixel? bitsPerPixel; + + /// + /// Indicates if run length compression should be used. + /// + private readonly TgaCompression compression; + + /// + /// Initializes a new instance of the class. + /// + /// The encoder options. + /// The memory manager. + public TgaEncoderCore(ITgaEncoderOptions options, MemoryAllocator memoryAllocator) + { + this.memoryAllocator = memoryAllocator; + this.bitsPerPixel = options.BitsPerPixel; + this.compression = options.Compression; + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(image, nameof(image)); + Guard.NotNull(stream, nameof(stream)); + + this.configuration = image.GetConfiguration(); + ImageMetadata metadata = image.Metadata; + TgaMetadata tgaMetadata = metadata.GetTgaMetadata(); + this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel; + + TgaImageType imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleTrueColor : TgaImageType.TrueColor; + if (this.bitsPerPixel == TgaBitsPerPixel.Pixel8) + { + imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite; + } + + // If compression is used, set bit 5 of the image descriptor to indicate an left top origin. + byte imageDescriptor = (byte)(this.compression is TgaCompression.RunLength ? 32 : 0); + + var fileHeader = new TgaFileHeader( + idLength: 0, + colorMapType: 0, + imageType: imageType, + cMapStart: 0, + cMapLength: 0, + cMapDepth: 0, + xOffset: 0, + yOffset: this.compression is TgaCompression.RunLength ? (short)image.Height : (short)0, // When run length encoding is used, the origin should be top left instead of the default bottom left. + width: (short)image.Width, + height: (short)image.Height, + pixelDepth: (byte)this.bitsPerPixel.Value, + imageDescriptor: imageDescriptor); + + Span buffer = stackalloc byte[TgaFileHeader.Size]; + fileHeader.WriteTo(buffer); + + stream.Write(buffer, 0, TgaFileHeader.Size); + + if (this.compression is TgaCompression.RunLength) + { + this.WriteRunLengthEndcodedImage(stream, image.Frames.RootFrame); + } + else + { + this.WriteImage(stream, image.Frames.RootFrame); + } + + stream.Flush(); + } + + /// + /// Writes the pixel data to the binary stream. + /// + /// The pixel format. + /// The to write to. + /// + /// The containing pixel data. + /// + private void WriteImage(Stream stream, ImageFrame image) + where TPixel : struct, IPixel + { + Buffer2D pixels = image.PixelBuffer; + switch (this.bitsPerPixel) + { + case TgaBitsPerPixel.Pixel8: + this.Write8Bit(stream, pixels); + break; + + case TgaBitsPerPixel.Pixel16: + this.Write16Bit(stream, pixels); + break; + + case TgaBitsPerPixel.Pixel24: + this.Write24Bit(stream, pixels); + break; + + case TgaBitsPerPixel.Pixel32: + this.Write32Bit(stream, pixels); + break; + } + } + + /// + /// Writes a run length encoded tga image to the stream. + /// + /// The pixel type. + /// The stream to write the image to. + /// The image to encode. + private void WriteRunLengthEndcodedImage(Stream stream, ImageFrame image) + where TPixel : struct, IPixel + { + Rgba32 color = default; + Buffer2D pixels = image.PixelBuffer; + Span pixelSpan = pixels.GetSpan(); + int totalPixels = image.Width * image.Height; + int encodedPixels = 0; + while (encodedPixels < totalPixels) + { + TPixel currentPixel = pixelSpan[encodedPixels]; + currentPixel.ToRgba32(ref color); + byte equalPixelCount = this.FindEqualPixels(pixelSpan.Slice(encodedPixels)); + + // Write the number of equal pixels, with the high bit set, indicating ist a compressed pixel run. + stream.WriteByte((byte)(equalPixelCount | 128)); + switch (this.bitsPerPixel) + { + case TgaBitsPerPixel.Pixel8: + int luminance = GetLuminance(currentPixel); + stream.WriteByte((byte)luminance); + break; + + case TgaBitsPerPixel.Pixel16: + var bgra5551 = new Bgra5551(color.ToVector4()); + BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); + stream.WriteByte(this.buffer[0]); + stream.WriteByte(this.buffer[1]); + + break; + + case TgaBitsPerPixel.Pixel24: + stream.WriteByte(color.B); + stream.WriteByte(color.G); + stream.WriteByte(color.R); + break; + + case TgaBitsPerPixel.Pixel32: + stream.WriteByte(color.B); + stream.WriteByte(color.G); + stream.WriteByte(color.R); + stream.WriteByte(color.A); + break; + } + + encodedPixels += equalPixelCount + 1; + } + } + + /// + /// Finds consecutive pixels, which have the same value starting from the pixel span offset 0. + /// + /// The pixel type. + /// The pixel span to search in. + /// The number of equal pixels. + private byte FindEqualPixels(Span pixelSpan) + where TPixel : struct, IPixel + { + int idx = 0; + byte equalPixelCount = 0; + while (equalPixelCount < 127 && idx < pixelSpan.Length - 1) + { + TPixel currentPixel = pixelSpan[idx]; + TPixel nextPixel = pixelSpan[idx + 1]; + if (currentPixel.Equals(nextPixel)) + { + equalPixelCount++; + } + else + { + return equalPixelCount; + } + + idx++; + } + + return equalPixelCount; + } + + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, 0); + + /// + /// Writes the 8bit pixels uncompressed to the stream. + /// + /// The pixel format. + /// The to write to. + /// The containing pixel data. + private void Write8Bit(Stream stream, Buffer2D pixels) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 1)) + { + for (int y = pixels.Height - 1; y >= 0; y--) + { + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.ToL8Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); + stream.Write(row.Array, 0, row.Length()); + } + } + } + + /// + /// Writes the 16bit pixels uncompressed to the stream. + /// + /// The pixel format. + /// The to write to. + /// The containing pixel data. + private void Write16Bit(Stream stream, Buffer2D pixels) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 2)) + { + for (int y = pixels.Height - 1; y >= 0; y--) + { + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.ToBgra5551Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); + stream.Write(row.Array, 0, row.Length()); + } + } + } + + /// + /// Writes the 24bit pixels uncompressed to the stream. + /// + /// The pixel format. + /// The to write to. + /// The containing pixel data. + private void Write24Bit(Stream stream, Buffer2D pixels) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3)) + { + for (int y = pixels.Height - 1; y >= 0; y--) + { + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.ToBgr24Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); + stream.Write(row.Array, 0, row.Length()); + } + } + } + + /// + /// Writes the 32bit pixels uncompressed to the stream. + /// + /// The pixel format. + /// The to write to. + /// The containing pixel data. + private void Write32Bit(Stream stream, Buffer2D pixels) + where TPixel : struct, IPixel + { + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4)) + { + for (int y = pixels.Height - 1; y >= 0; y--) + { + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.ToBgra32Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); + stream.Write(row.Array, 0, row.Length()); + } + } + } + + /// + /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. + /// + /// The pixel to get the luminance from. + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetLuminance(TPixel sourcePixel) + where TPixel : struct, IPixel + { + var vector = sourcePixel.ToVector4(); + return ImageMaths.GetBT709Luminance(ref vector, 256); + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaFileHeader.cs b/src/ImageSharp/Formats/Tga/TgaFileHeader.cs new file mode 100644 index 0000000000..e2bbb6fbd2 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaFileHeader.cs @@ -0,0 +1,147 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// This block of bytes tells the application detailed information about the targa image. + /// + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal readonly struct TgaFileHeader + { + /// + /// Defines the size of the data structure in the targa file. + /// + public const int Size = TgaConstants.FileHeaderLength; + + public TgaFileHeader( + byte idLength, + byte colorMapType, + TgaImageType imageType, + short cMapStart, + short cMapLength, + byte cMapDepth, + short xOffset, + short yOffset, + short width, + short height, + byte pixelDepth, + byte imageDescriptor) + { + this.IdLength = idLength; + this.ColorMapType = colorMapType; + this.ImageType = imageType; + this.CMapStart = cMapStart; + this.CMapLength = cMapLength; + this.CMapDepth = cMapDepth; + this.XOffset = xOffset; + this.YOffset = yOffset; + this.Width = width; + this.Height = height; + this.PixelDepth = pixelDepth; + this.ImageDescriptor = imageDescriptor; + } + + /// + /// Gets the id length. + /// This field identifies the number of bytes contained in Field 6, the Image ID Field. The maximum number + /// of characters is 255. A value of zero indicates that no Image ID field is included with the image. + /// + public byte IdLength { get; } + + /// + /// Gets the color map type. + /// This field indicates the type of color map (if any) included with the image. There are currently 2 defined + /// values for this field: + /// 0 - indicates that no color-map data is included with this image. + /// 1 - indicates that a color-map is included with this image. + /// + public byte ColorMapType { get; } + + /// + /// Gets the image type. + /// The TGA File Format can be used to store Pseudo-Color, True-Color and Direct-Color images of various + /// pixel depths. + /// + public TgaImageType ImageType { get; } + + /// + /// Gets the start of the color map. + /// This field and its sub-fields describe the color map (if any) used for the image. If the Color Map Type field + /// is set to zero, indicating that no color map exists, then these 5 bytes should be set to zero. + /// + public short CMapStart { get; } + + /// + /// Gets the total number of color map entries included. + /// + public short CMapLength { get; } + + /// + /// Gets the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used. + /// + public byte CMapDepth { get; } + + /// + /// Gets the XOffset. + /// These bytes specify the absolute horizontal coordinate for the lower left + /// corner of the image as it is positioned on a display device having an + /// origin at the lower left of the screen. + /// + public short XOffset { get; } + + /// + /// Gets the YOffset. + /// These bytes specify the absolute vertical coordinate for the lower left + /// corner of the image as it is positioned on a display device having an + /// origin at the lower left of the screen. + /// + public short YOffset { get; } + + /// + /// Gets the width of the image in pixels. + /// + public short Width { get; } + + /// + /// Gets the height of the image in pixels. + /// + public short Height { get; } + + /// + /// Gets the number of bits per pixel. This number includes + /// the Attribute or Alpha channel bits. Common values are 8, 16, 24 and + /// 32 but other pixel depths could be used. + /// + public byte PixelDepth { get; } + + /// + /// Gets the ImageDescriptor. + /// ImageDescriptor contains two pieces of information. + /// Bits 0 through 3 contain the number of attribute bits per pixel. + /// Attribute bits are found only in pixels for the 16- and 32-bit flavors of the TGA format and are called alpha channel, + /// overlay, or interrupt bits. Bits 4 and 5 contain the image origin location (coordinate 0,0) of the image. + /// This position may be any of the four corners of the display screen. + /// When both of these bits are set to zero, the image origin is the lower-left corner of the screen. + /// Bits 6 and 7 of the ImageDescriptor field are unused and should be set to 0. + /// + public byte ImageDescriptor { get; } + + public static TgaFileHeader Parse(Span data) + { + return MemoryMarshal.Cast(data)[0]; + } + + public void WriteTo(Span buffer) + { + ref TgaFileHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer)); + + dest = this; + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaFormat.cs b/src/ImageSharp/Formats/Tga/TgaFormat.cs new file mode 100644 index 0000000000..badb1d77a4 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaFormat.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Registers the image encoders, decoders and mime type detectors for the tga format. + /// + public sealed class TgaFormat : IImageFormat + { + /// + /// Gets the current instance. + /// + public static TgaFormat Instance { get; } = new TgaFormat(); + + /// + public string Name => "TGA"; + + /// + public string DefaultMimeType => "image/tga"; + + /// + public IEnumerable MimeTypes => TgaConstants.MimeTypes; + + /// + public IEnumerable FileExtensions => TgaConstants.FileExtensions; + + /// + public TgaMetadata CreateDefaultFormatMetadata() => new TgaMetadata(); + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaImageFormatDetector.cs b/src/ImageSharp/Formats/Tga/TgaImageFormatDetector.cs new file mode 100644 index 0000000000..af40c333b6 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaImageFormatDetector.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Detects tga file headers. + /// + public sealed class TgaImageFormatDetector : IImageFormatDetector + { + /// + public int HeaderSize => TgaConstants.FileHeaderLength; + + /// + public IImageFormat DetectFormat(ReadOnlySpan header) + { + return this.IsSupportedFileFormat(header) ? TgaFormat.Instance : null; + } + + private bool IsSupportedFileFormat(ReadOnlySpan header) + { + if (header.Length >= this.HeaderSize) + { + // There are no magic bytes in a tga file, so at least the image type + // and the colormap type in the header will be checked for a valid value. + if (header[1] != 0 && header[1] != 1) + { + return false; + } + + var imageType = (TgaImageType)header[2]; + return imageType.IsValid(); + } + + return false; + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaImageType.cs b/src/ImageSharp/Formats/Tga/TgaImageType.cs new file mode 100644 index 0000000000..491fd3ea77 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaImageType.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors. + ImageSharp.Formats.Tga +{ + /// + /// Defines the tga image type. The TGA File Format can be used to store Pseudo-Color, + /// True-Color and Direct-Color images of various pixel depths. + /// + public enum TgaImageType : byte + { + /// + /// No image data included. + /// + NoImageData = 0, + + /// + /// Uncompressed, color mapped image. + /// + ColorMapped = 1, + + /// + /// Uncompressed true color image. + /// + TrueColor = 2, + + /// + /// Uncompressed Black and white (grayscale) image. + /// + BlackAndWhite = 3, + + /// + /// Run length encoded, color mapped image. + /// + RleColorMapped = 9, + + /// + /// Run length encoded, true color image. + /// + RleTrueColor = 10, + + /// + /// Run length encoded, black and white (grayscale) image. + /// + RleBlackAndWhite = 11, + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaImageTypeExtensions.cs b/src/ImageSharp/Formats/Tga/TgaImageTypeExtensions.cs new file mode 100644 index 0000000000..6a30cdddd7 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaImageTypeExtensions.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Extension methods for TgaImageType enum. + /// + public static class TgaImageTypeExtensions + { + /// + /// Checks if this tga image type is run length encoded. + /// + /// The tga image type. + /// True, if this image type is run length encoded, otherwise false. + public static bool IsRunLengthEncoded(this TgaImageType imageType) + { + if (imageType is TgaImageType.RleColorMapped || imageType is TgaImageType.RleBlackAndWhite || imageType is TgaImageType.RleTrueColor) + { + return true; + } + + return false; + } + + /// + /// Checks, if the image type has valid value. + /// + /// The image type. + /// true, if its a valid tga image type. + public static bool IsValid(this TgaImageType imageType) + { + switch (imageType) + { + case TgaImageType.NoImageData: + case TgaImageType.ColorMapped: + case TgaImageType.TrueColor: + case TgaImageType.BlackAndWhite: + case TgaImageType.RleColorMapped: + case TgaImageType.RleTrueColor: + case TgaImageType.RleBlackAndWhite: + return true; + + default: + return false; + } + } + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaMetadata.cs b/src/ImageSharp/Formats/Tga/TgaMetadata.cs new file mode 100644 index 0000000000..4ce61d2e48 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaMetadata.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Tga +{ + /// + /// Provides TGA specific metadata information for the image. + /// + public class TgaMetadata : IDeepCloneable + { + /// + /// Initializes a new instance of the class. + /// + public TgaMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The metadata to create an instance from. + private TgaMetadata(TgaMetadata other) + { + this.BitsPerPixel = other.BitsPerPixel; + } + + /// + /// Gets or sets the number of bits per pixel. + /// + public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24; + + /// + public IDeepCloneable DeepClone() => new TgaMetadata(this); + } +} diff --git a/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs b/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs new file mode 100644 index 0000000000..845d009227 --- /dev/null +++ b/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Tga +{ + internal static class TgaThrowHelper + { + /// + /// Cold path optimization for throwing -s + /// + /// The error message for the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowImageFormatException(string errorMessage) + { + throw new ImageFormatException(errorMessage); + } + + /// + /// Cold path optimization for throwing -s + /// + /// The error message for the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowNotSupportedException(string errorMessage) + { + throw new NotSupportedException(errorMessage); + } + } +} diff --git a/src/ImageSharp/GeometryUtilities.cs b/src/ImageSharp/GeometryUtilities.cs new file mode 100644 index 0000000000..43c46f1812 --- /dev/null +++ b/src/ImageSharp/GeometryUtilities.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors +{ + /// + /// Utility class for common geometric functions. + /// + public static class GeometryUtilities + { + /// + /// 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) => degree * (MathF.PI / 180F); + + /// + /// 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) => radian / (MathF.PI / 180F); + } +} \ No newline at end of file diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index 214b10810a..47b930e654 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -8,170 +8,82 @@ namespace SixLabors.ImageSharp /// /// Options for influencing the drawing functions. /// - public struct GraphicsOptions + public class GraphicsOptions : IDeepCloneable { - /// - /// Represents the default . - /// - public static readonly GraphicsOptions Default = new GraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private PixelColorBlendingMode colorBlendingMode; - - private PixelAlphaCompositionMode alphaCompositionMode; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public GraphicsOptions(bool enableAntialiasing) - { - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } + private int antialiasSubpixelDepth = 16; + private float blendPercentage = 1F; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the class. /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) + public GraphicsOptions() { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = blending; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; } - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - /// alpha composition mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) + private GraphicsOptions(GraphicsOptions source) { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = blending; - this.alphaCompositionMode = composition; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; + this.AlphaCompositionMode = source.AlphaCompositionMode; + this.Antialias = source.Antialias; + this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth; + this.BlendPercentage = source.BlendPercentage; + this.ColorBlendingMode = source.ColorBlendingMode; } /// /// Gets or sets a value indicating whether antialiasing should be applied. + /// Defaults to true. /// - public bool Antialias - { - get => this.antialias ?? true; - set => this.antialias = value; - } + public bool Antialias { get; set; } = true; /// /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// Defaults to 16. /// public int AntialiasSubpixelDepth { - get => this.antialiasSubpixelDepth ?? 16; - set => this.antialiasSubpixelDepth = value; + get + { + return this.antialiasSubpixelDepth; + } + + set + { + Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth)); + this.antialiasSubpixelDepth = value; + } } /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// Gets or sets a value between indicating the blending percentage to apply to the drawing operation. + /// Range 0..1; Defaults to 1. /// public float BlendPercentage { - get => (this.blendPercentage ?? 1).Clamp(0, 1); - set => this.blendPercentage = value; - } + get + { + return this.blendPercentage; + } - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. + set + { + Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage)); + this.blendPercentage = value; + } + } /// - /// Gets or sets a value indicating the color blending mode to apply to the drawing operation + /// Gets or sets a value indicating the color blending mode to apply to the drawing operation. + /// Defaults to . /// - public PixelColorBlendingMode ColorBlendingMode - { - get => this.colorBlendingMode; - set => this.colorBlendingMode = value; - } + public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal; /// /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation + /// Defaults to . /// - public PixelAlphaCompositionMode AlphaCompositionMode - { - get => this.alphaCompositionMode; - set => this.alphaCompositionMode = value; - } + public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver; - /// - /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. - /// - /// the color - /// true if the color can be considered opaque - /// - /// Blending and composition is an expensive operation, in some cases, like - /// filling with a solid color, the blending can be avoided by a plain color replacement. - /// This method can be useful for such processors to select the fast path. - /// - internal bool IsOpaqueColorWithoutBlending(Color color) - { - if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) - { - return false; - } - - if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && - this.AlphaCompositionMode != PixelAlphaCompositionMode.Src) - { - return false; - } - - if (this.BlendPercentage != 1f) - { - return false; - } - - if (color.ToVector4().W != 1f) - { - return false; - } - - return true; - } + /// + public GraphicsOptions DeepClone() => new GraphicsOptions(this); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs index 07f8928068..0345717d21 100644 --- a/src/ImageSharp/IO/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using System.Runtime.CompilerServices; -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.IO { diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 8d0df599ea..e1376b4a25 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -1,13 +1,13 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp { @@ -47,19 +47,26 @@ namespace SixLabors.ImageSharp /// The mime type or null if none found. private static IImageFormat InternalDetectFormat(Stream stream, Configuration config) { - // This is probably a candidate for making into a public API in the future! - int maxHeaderSize = config.MaxHeaderSize; - if (maxHeaderSize <= 0) + // We take a minimum of the stream length vs the max header size and always check below + // to ensure that only formats that headers fit within the given buffer length are tested. + int headerSize = (int)Math.Min(config.MaxHeaderSize, stream.Length); + if (headerSize <= 0) { return null; } - using (IManagedByteBuffer buffer = config.MemoryAllocator.AllocateManagedByteBuffer(maxHeaderSize, AllocationOptions.Clean)) + using (IManagedByteBuffer buffer = config.MemoryAllocator.AllocateManagedByteBuffer(headerSize, AllocationOptions.Clean)) { long startPosition = stream.Position; - stream.Read(buffer.Array, 0, maxHeaderSize); + stream.Read(buffer.Array, 0, headerSize); stream.Position = startPosition; - return config.ImageFormatsManager.FormatDetectors.Select(x => x.DetectFormat(buffer.GetSpan())).LastOrDefault(x => x != null); + + // Does the given stream contain enough data to fit in the header for the format + // and does that data match the format specification? + // Individual formats should still check since they are public. + return config.ImageFormatsManager.FormatDetectors + .Where(x => x.HeaderSize <= headerSize) + .Select(x => x.DetectFormat(buffer.GetSpan())).LastOrDefault(x => x != null); } } @@ -123,10 +130,14 @@ namespace SixLabors.ImageSharp /// /// The or null if suitable info detector not found. /// - private static IImageInfo InternalIdentity(Stream stream, Configuration config) + private static (IImageInfo info, IImageFormat format) InternalIdentity(Stream stream, Configuration config) { - var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector; - return detector?.Identify(config, stream); + if (!(DiscoverDecoder(stream, config, out IImageFormat format) is IImageInfoDetector detector)) + { + return (null, null); + } + + return (detector?.Identify(config, stream), format); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 465139b2e5..389fbba6d5 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data) where TPixel : struct, IPixel { - using (var stream = new MemoryStream(data)) + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { return Load(config, stream); } @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data, out IImageFormat format) where TPixel : struct, IPixel { - using (var stream = new MemoryStream(data)) + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { return Load(config, stream, out format); } @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp public static Image Load(byte[] data, IImageDecoder decoder) where TPixel : struct, IPixel { - using (var stream = new MemoryStream(data)) + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { return Load(stream, decoder); } @@ -125,9 +125,9 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) where TPixel : struct, IPixel { - using (var memoryStream = new MemoryStream(data)) + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { - return Load(config, memoryStream, decoder); + return Load(config, stream, decoder); } } @@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp /// /// The byte array containing image data. /// The detected format. - /// A new . + /// The . public static Image Load(byte[] data, out IImageFormat format) => Load(Configuration.Default, data, out format); @@ -279,52 +279,52 @@ namespace SixLabors.ImageSharp /// /// The byte array containing encoded image data. /// The decoder. - /// A new . + /// The . public static Image Load(byte[] data, IImageDecoder decoder) => Load(Configuration.Default, data, decoder); /// - /// Load a new instance of from the given encoded byte array. + /// Load a new instance of from the given encoded byte array. /// /// The config for the decoder. /// The byte array containing encoded image data. - /// A new . + /// The . public static Image Load(Configuration config, byte[] data) => Load(config, data, out _); /// - /// Load a new instance of from the given encoded byte array. + /// Load a new instance of from the given encoded byte array. /// /// The config for the decoder. /// The byte array containing image data. /// The decoder. - /// A new . + /// The . public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) { - using (var stream = new MemoryStream(data)) + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { return Load(config, stream, decoder); } } /// - /// Load a new instance of from the given encoded byte array. + /// Load a new instance of from the given encoded byte array. /// /// The config for the decoder. /// The byte array containing image data. /// The mime type of the decoded image. - /// A new . + /// The . public static Image Load(Configuration config, byte[] data, out IImageFormat format) { - using (var stream = new MemoryStream(data)) + using (var stream = new MemoryStream(data, 0, data.Length, false, true)) { return Load(config, stream, out format); } } /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte span. /// /// The byte span containing image data. - /// A new . + /// The . public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data); /// @@ -332,7 +332,7 @@ namespace SixLabors.ImageSharp /// /// The byte span containing image data. /// The decoder. - /// A new . + /// The . public static Image Load(ReadOnlySpan data, IImageDecoder decoder) => Load(Configuration.Default, data, decoder); @@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp /// /// The byte span containing image data. /// The detected format. - /// A new . + /// The . public static Image Load(ReadOnlySpan data, out IImageFormat format) => Load(Configuration.Default, data, out format); @@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp /// /// The configuration options. /// The byte span containing image data. - /// A new . + /// The . public static Image Load(Configuration config, ReadOnlySpan data) => Load(config, data, out _); /// @@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp /// The Configuration. /// The byte span containing image data. /// The decoder. - /// A new . + /// The . public static unsafe Image Load( Configuration config, ReadOnlySpan data, @@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp /// The configuration options. /// The byte span containing image data. /// The of the decoded image.> - /// A new . + /// The . public static unsafe Image Load( Configuration config, ReadOnlySpan data, diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 08ed381c5a..95b4b9790e 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -39,17 +39,17 @@ namespace SixLabors.ImageSharp } /// - /// Create a new instance of the class from the given file. + /// Create a new instance of the class from the given file. /// /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// - /// A new . + /// The . public static Image Load(string path) => Load(Configuration.Default, path); /// - /// Create a new instance of the class from the given file. + /// Create a new instance of the class from the given file. /// /// The file path to the image. /// The mime type of the decoded image. @@ -60,18 +60,18 @@ namespace SixLabors.ImageSharp public static Image Load(string path, out IImageFormat format) => Load(Configuration.Default, path, out format); /// - /// Create a new instance of the class from the given file. + /// Create a new instance of the class from the given file. /// /// The config for the decoder. /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// - /// A new . + /// The . public static Image Load(Configuration config, string path) => Load(config, path, out _); /// - /// Create a new instance of the class from the given file. + /// Create a new instance of the class from the given file. /// /// The Configuration. /// The file path to the image. @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp /// /// Thrown if the stream is not readable nor seekable. /// - /// A new . + /// The . public static Image Load(Configuration config, string path, IImageDecoder decoder) { using (Stream stream = config.FileSystem.OpenRead(path)) @@ -89,14 +89,14 @@ namespace SixLabors.ImageSharp } /// - /// Create a new instance of the class from the given file. + /// Create a new instance of the class from the given file. /// /// The file path to the image. /// The decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// A new . + /// The . public static Image Load(string path, IImageDecoder decoder) => Load(Configuration.Default, path, decoder); /// @@ -224,4 +224,4 @@ namespace SixLabors.ImageSharp } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index c4336c9aca..db2cc2fd19 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -16,20 +16,20 @@ namespace SixLabors.ImageSharp public abstract partial class Image { /// - /// By reading the header on the provided stream this calculates the images mime type. + /// By reading the header on the provided stream this calculates the images format type. /// /// The image stream to read the header from. /// Thrown if the stream is not readable. - /// The mime type or null if none found. + /// The format type or null if none found. public static IImageFormat DetectFormat(Stream stream) => DetectFormat(Configuration.Default, stream); /// - /// By reading the header on the provided stream this calculates the images mime type. + /// By reading the header on the provided stream this calculates the images format type. /// /// The configuration. /// The image stream to read the header from. /// Thrown if the stream is not readable. - /// The mime type or null if none found. + /// The format type or null if none found. public static IImageFormat DetectFormat(Configuration config, Stream stream) => WithSeekableStream(config, stream, s => InternalDetectFormat(s, config)); @@ -41,29 +41,46 @@ namespace SixLabors.ImageSharp /// /// The or null if suitable info detector not found. /// - public static IImageInfo Identify(Stream stream) => Identify(Configuration.Default, stream); + public static IImageInfo Identify(Stream stream) => Identify(stream, out IImageFormat _); + + /// + /// By reading the header on the provided stream this reads the raw image information. + /// + /// The image stream to read the header from. + /// The format type of the decoded image. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector not found. + /// + public static IImageInfo Identify(Stream stream, out IImageFormat format) => Identify(Configuration.Default, stream, out format); /// /// Reads the raw image information from the specified stream without fully decoding it. /// /// The configuration. /// The image stream to read the information from. + /// The format type of the decoded image. /// Thrown if the stream is not readable. /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration config, Stream stream) - => WithSeekableStream(config, stream, s => InternalIdentity(s, config ?? Configuration.Default)); + public static IImageInfo Identify(Configuration config, Stream stream, out IImageFormat format) + { + (IImageInfo info, IImageFormat format) data = WithSeekableStream(config, stream, s => InternalIdentity(s, config ?? Configuration.Default)); + + format = data.format; + return data.info; + } /// /// Decode a new instance of the class from the given stream. /// The pixel format is selected by the decoder. /// /// The stream containing image information. - /// the mime type of the decoded image. + /// The format type of the decoded image. /// Thrown if the stream is not readable. /// Image cannot be loaded. - /// A new .> + /// The . public static Image Load(Stream stream, out IImageFormat format) => Load(Configuration.Default, stream, out format); /// @@ -73,7 +90,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// Thrown if the stream is not readable. /// Image cannot be loaded. - /// A new .> + /// The . public static Image Load(Stream stream) => Load(Configuration.Default, stream); /// @@ -84,7 +101,7 @@ namespace SixLabors.ImageSharp /// The decoder. /// Thrown if the stream is not readable. /// Image cannot be loaded. - /// A new .> + /// The . public static Image Load(Stream stream, IImageDecoder decoder) => Load(Configuration.Default, stream, decoder); /// @@ -126,7 +143,7 @@ namespace SixLabors.ImageSharp /// Create a new instance of the class from the given stream. /// /// The stream containing image information. - /// the mime type of the decoded image. + /// The format type of the decoded image. /// Thrown if the stream is not readable. /// Image cannot be loaded. /// The pixel format. @@ -180,7 +197,7 @@ namespace SixLabors.ImageSharp /// /// The configuration options. /// The stream containing image information. - /// the mime type of the decoded image. + /// The format type of the decoded image. /// Thrown if the stream is not readable. /// Image cannot be loaded. /// The pixel format. @@ -215,7 +232,7 @@ namespace SixLabors.ImageSharp /// /// The configuration options. /// The stream containing image information. - /// the mime type of the decoded image. + /// The format type of the decoded image. /// Thrown if the stream is not readable. /// Image cannot be loaded. /// A new . diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index d7fed90164..a62bfed1e2 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -8,7 +8,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/ImageFrame.cs b/src/ImageSharp/ImageFrame.cs index 91872b21d6..fe2a2b7626 100644 --- a/src/ImageSharp/ImageFrame.cs +++ b/src/ImageSharp/ImageFrame.cs @@ -2,11 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; - +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index c5bd02c79d..c584d2d193 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp public IEnumerator GetEnumerator() => this.NonGenericGetEnumerator(); /// - IEnumerator IEnumerable.GetEnumerator() => this.NonGenericGetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); /// /// Implements . diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs index 893a7a4a80..722a4ddea8 100644 --- a/src/ImageSharp/ImageFrameCollection{TPixel}.cs +++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp this.parent.GetConfiguration(), source.Size(), source.Metadata.DeepClone()); - source.CopyPixelsTo(result.PixelBuffer.Span); + source.CopyPixelsTo(result.PixelBuffer.GetSpan()); return result; } } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 0436eb9d2b..85454e1508 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -6,12 +6,10 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -218,10 +216,10 @@ namespace SixLabors.ImageSharp if (typeof(TPixel) == typeof(TDestinationPixel)) { Span dest1 = MemoryMarshal.Cast(destination); - this.PixelBuffer.Span.CopyTo(dest1); + this.PixelBuffer.GetSpan().CopyTo(dest1); } - PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.Span, destination); + PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.GetSpan(), destination); } /// diff --git a/src/ImageSharp/ImageInfoExtensions.cs b/src/ImageSharp/ImageInfoExtensions.cs index dca5502d0f..abaa7c4bc4 100644 --- a/src/ImageSharp/ImageInfoExtensions.cs +++ b/src/ImageSharp/ImageInfoExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp { /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 86b0848663..0fd449d90f 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -1,4 +1,4 @@ - + @@ -10,7 +10,7 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0;net472 + netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;netstandard1.3;net472 true true @@ -19,25 +19,21 @@ SixLabors.ImageSharp - - - $(DefineConstants);SUPPORTS_MATHF - - - - $(DefineConstants);SUPPORTS_HASHCODE - - - - $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS - + + + - - + + + + - - + + + + + @@ -76,15 +72,30 @@ True Bgra32.PixelOperations.Generated.tt - + + True + True + Bgra5551.PixelOperations.Generated.tt + + + True + True + L8.PixelOperations.Generated.tt + + + True + True + L16.PixelOperations.Generated.tt + + True True - Gray8.PixelOperations.Generated.tt + La16.PixelOperations.Generated.tt - + True True - Gray16.PixelOperations.Generated.tt + La32.PixelOperations.Generated.tt True @@ -118,10 +129,6 @@ - - - - TextTemplatingFileGenerator @@ -151,13 +158,25 @@ TextTemplatingFileGenerator Bgra32.PixelOperations.Generated.cs - + TextTemplatingFileGenerator - Gray8.PixelOperations.Generated.cs + Bgra5551.PixelOperations.Generated.cs - + TextTemplatingFileGenerator - Gray16.PixelOperations.Generated.cs + L8.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + L16.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + La16.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + La32.PixelOperations.Generated.cs TextTemplatingFileGenerator @@ -185,21 +204,10 @@ - - - - - - - - - - - - - + + diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 3f733479dc..b7e63dc253 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -9,7 +9,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -130,7 +129,7 @@ namespace SixLabors.ImageSharp protected override ImageFrameCollection NonGenericFrameCollection => this.Frames; /// - /// Gets the frames. + /// Gets the collection of image frames. /// public new ImageFrameCollection Frames { get; } @@ -166,8 +165,12 @@ namespace SixLabors.ImageSharp { this.EnsureNotDisposed(); - IEnumerable> clonedFrames = - this.Frames.Select, ImageFrame>(x => x.Clone(configuration)); + var clonedFrames = new ImageFrame[this.Frames.Count]; + for (int i = 0; i < clonedFrames.Length; i++) + { + clonedFrames[i] = this.Frames[i].Clone(configuration); + } + return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } @@ -181,8 +184,12 @@ namespace SixLabors.ImageSharp { this.EnsureNotDisposed(); - IEnumerable> clonedFrames = - this.Frames.Select, ImageFrame>(x => x.CloneAs(configuration)); + var clonedFrames = new ImageFrame[this.Frames.Count]; + for (int i = 0; i < clonedFrames.Length; i++) + { + clonedFrames[i] = this.Frames[i].CloneAs(configuration); + } + return new Image(configuration, this.Metadata.DeepClone(), clonedFrames); } diff --git a/src/ImageSharp/Memory/Allocators/AllocationOptions.cs b/src/ImageSharp/Memory/Allocators/AllocationOptions.cs new file mode 100644 index 0000000000..4edb702ed9 --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/AllocationOptions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Options for allocating buffers. + /// + public enum AllocationOptions + { + /// + /// Indicates that the buffer should just be allocated. + /// + None, + + /// + /// Indicates that the allocated buffer should be cleaned following allocation. + /// + Clean + } +} diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs new file mode 100644 index 0000000000..0d7e0b784f --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.Buffer{T}.cs @@ -0,0 +1,85 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory.Internals; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Contains and . + /// + public partial class ArrayPoolMemoryAllocator + { + /// + /// The buffer implementation of . + /// + private class Buffer : ManagedBufferBase + where T : struct + { + /// + /// The length of the buffer. + /// + private readonly int length; + + /// + /// A weak reference to the source pool. + /// + /// + /// By using a weak reference here, we are making sure that array pools and their retained arrays are always GC-ed + /// after a call to , regardless of having buffer instances still being in use. + /// + private WeakReference> sourcePoolReference; + + public Buffer(byte[] data, int length, ArrayPool sourcePool) + { + this.Data = data; + this.length = length; + this.sourcePoolReference = new WeakReference>(sourcePool); + } + + /// + /// Gets the buffer as a byte array. + /// + protected byte[] Data { get; private set; } + + /// + public override Span GetSpan() => MemoryMarshal.Cast(this.Data.AsSpan()).Slice(0, this.length); + + /// + protected override void Dispose(bool disposing) + { + if (!disposing || this.Data is null || this.sourcePoolReference is null) + { + return; + } + + if (this.sourcePoolReference.TryGetTarget(out ArrayPool pool)) + { + pool.Return(this.Data); + } + + this.sourcePoolReference = null; + this.Data = null; + } + + protected override object GetPinnableObject() => this.Data; + } + + /// + /// The implementation of . + /// + private sealed class ManagedByteBuffer : Buffer, IManagedByteBuffer + { + public ManagedByteBuffer(byte[] data, int length, ArrayPool sourcePool) + : base(data, length, sourcePool) + { + } + + /// + public byte[] Array => this.Data; + } + } +} diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs new file mode 100644 index 0000000000..1ce2525b82 --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Contains common factory methods and configuration constants. + /// + public partial class ArrayPoolMemoryAllocator + { + /// + /// The default value for: maximum size of pooled arrays in bytes. + /// Currently set to 24MB, which is equivalent to 8 megapixels of raw RGBA32 data. + /// + internal const int DefaultMaxPooledBufferSizeInBytes = 24 * 1024 * 1024; + + /// + /// The value for: The threshold to pool arrays in which has less buckets for memory safety. + /// + private const int DefaultBufferSelectorThresholdInBytes = 8 * 1024 * 1024; + + /// + /// The default bucket count for . + /// + private const int DefaultLargePoolBucketCount = 6; + + /// + /// The default bucket count for . + /// + private const int DefaultNormalPoolBucketCount = 16; + + /// + /// This is the default. Should be good for most use cases. + /// + /// The memory manager. + public static ArrayPoolMemoryAllocator CreateDefault() + { + return new ArrayPoolMemoryAllocator( + DefaultMaxPooledBufferSizeInBytes, + DefaultBufferSelectorThresholdInBytes, + DefaultLargePoolBucketCount, + DefaultNormalPoolBucketCount); + } + + /// + /// For environments with very limited memory capabilities, only small buffers like image rows are pooled. + /// + /// The memory manager. + public static ArrayPoolMemoryAllocator CreateWithMinimalPooling() + { + return new ArrayPoolMemoryAllocator(64 * 1024, 32 * 1024, 8, 24); + } + + /// + /// For environments with limited memory capabilities, only small array requests are pooled, which can result in reduced throughput. + /// + /// The memory manager. + public static ArrayPoolMemoryAllocator CreateWithModeratePooling() + { + return new ArrayPoolMemoryAllocator(1024 * 1024, 32 * 1024, 16, 24); + } + + /// + /// For environments where memory capabilities are not an issue, the maximum amount of array requests are pooled which results in optimal throughput. + /// + /// The memory manager. + public static ArrayPoolMemoryAllocator CreateWithAggressivePooling() + { + return new ArrayPoolMemoryAllocator(128 * 1024 * 1024, 32 * 1024 * 1024, 16, 32); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs new file mode 100644 index 0000000000..c4d92ca3c5 --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs @@ -0,0 +1,150 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Implements by allocating memory from . + /// + public sealed partial class ArrayPoolMemoryAllocator : MemoryAllocator + { + private readonly int maxArraysPerBucketNormalPool; + + private readonly int maxArraysPerBucketLargePool; + + /// + /// The for small-to-medium buffers which is not kept clean. + /// + private ArrayPool normalArrayPool; + + /// + /// The for huge buffers, which is not kept clean. + /// + private ArrayPool largeArrayPool; + + /// + /// Initializes a new instance of the class. + /// + public ArrayPoolMemoryAllocator() + : this(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. + public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes) + : this(maxPoolSizeInBytes, GetLargeBufferThresholdInBytes(maxPoolSizeInBytes)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. + /// Arrays over this threshold will be pooled in which has less buckets for memory safety. + public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes) + : this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, DefaultLargePoolBucketCount, DefaultNormalPoolBucketCount) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. + /// The threshold to pool arrays in which has less buckets for memory safety. + /// Max arrays per bucket for the large array pool. + /// Max arrays per bucket for the normal array pool. + public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes, int maxArraysPerBucketLargePool, int maxArraysPerBucketNormalPool) + { + Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); + Guard.MustBeLessThanOrEqualTo(poolSelectorThresholdInBytes, maxPoolSizeInBytes, nameof(poolSelectorThresholdInBytes)); + + this.MaxPoolSizeInBytes = maxPoolSizeInBytes; + this.PoolSelectorThresholdInBytes = poolSelectorThresholdInBytes; + this.maxArraysPerBucketLargePool = maxArraysPerBucketLargePool; + this.maxArraysPerBucketNormalPool = maxArraysPerBucketNormalPool; + + this.InitArrayPools(); + } + + /// + /// Gets the maximum size of pooled arrays in bytes. + /// + public int MaxPoolSizeInBytes { get; } + + /// + /// Gets the threshold to pool arrays in which has less buckets for memory safety. + /// + public int PoolSelectorThresholdInBytes { get; } + + /// + public override void ReleaseRetainedResources() + { + this.InitArrayPools(); + } + + /// + public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + { + Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); + int itemSizeBytes = Unsafe.SizeOf(); + int bufferSizeInBytes = length * itemSizeBytes; + if (bufferSizeInBytes < 0) + { + throw new ArgumentOutOfRangeException( + nameof(length), + $"{nameof(ArrayPoolMemoryAllocator)} can not allocate {length} elements of {typeof(T).Name}."); + } + + ArrayPool pool = this.GetArrayPool(bufferSizeInBytes); + byte[] byteArray = pool.Rent(bufferSizeInBytes); + + var buffer = new Buffer(byteArray, length, pool); + if (options == AllocationOptions.Clean) + { + buffer.GetSpan().Clear(); + } + + return buffer; + } + + /// + public override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None) + { + Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); + + ArrayPool pool = this.GetArrayPool(length); + byte[] byteArray = pool.Rent(length); + + var buffer = new ManagedByteBuffer(byteArray, length, pool); + if (options == AllocationOptions.Clean) + { + buffer.GetSpan().Clear(); + } + + return buffer; + } + + private static int GetLargeBufferThresholdInBytes(int maxPoolSizeInBytes) + { + return maxPoolSizeInBytes / 4; + } + + private ArrayPool GetArrayPool(int bufferSizeInBytes) + { + return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; + } + + private void InitArrayPools() + { + this.largeArrayPool = ArrayPool.Create(this.MaxPoolSizeInBytes, this.maxArraysPerBucketLargePool); + this.normalArrayPool = ArrayPool.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Allocators/IManagedByteBuffer.cs b/src/ImageSharp/Memory/Allocators/IManagedByteBuffer.cs new file mode 100644 index 0000000000..cb1f58ddba --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/IManagedByteBuffer.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Represents a byte buffer backed by a managed array. Useful for interop with classic .NET API-s. + /// + public interface IManagedByteBuffer : IMemoryOwner + { + /// + /// Gets the managed array backing this buffer instance. + /// + byte[] Array { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs b/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs new file mode 100644 index 0000000000..56057f3726 --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/Internals/BasicArrayBuffer.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Memory.Internals +{ + /// + /// Wraps an array as an instance. + /// + /// + internal class BasicArrayBuffer : ManagedBufferBase + where T : struct + { + /// + /// Initializes a new instance of the class. + /// + /// The array. + /// The length of the buffer. + public BasicArrayBuffer(T[] array, int length) + { + DebugGuard.MustBeLessThanOrEqualTo(length, array.Length, nameof(length)); + this.Array = array; + this.Length = length; + } + + /// + /// Initializes a new instance of the class. + /// + /// The array. + public BasicArrayBuffer(T[] array) + : this(array, array.Length) + { + } + + /// + /// Gets the array. + /// + public T[] Array { get; } + + /// + /// Gets the length. + /// + public int Length { get; } + + /// + public override Span GetSpan() => this.Array.AsSpan(0, this.Length); + + /// + protected override void Dispose(bool disposing) + { + } + + /// + protected override object GetPinnableObject() + { + return this.Array; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Allocators/Internals/BasicByteBuffer.cs b/src/ImageSharp/Memory/Allocators/Internals/BasicByteBuffer.cs new file mode 100644 index 0000000000..571ad70c5d --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/Internals/BasicByteBuffer.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Memory.Internals +{ + /// + /// Provides an based on . + /// + internal sealed class BasicByteBuffer : BasicArrayBuffer, IManagedByteBuffer + { + /// + /// Initializes a new instance of the class. + /// + /// The byte array. + internal BasicByteBuffer(byte[] array) + : base(array) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs b/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs new file mode 100644 index 0000000000..8909638600 --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/Internals/ManagedBufferBase.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Memory.Internals +{ + /// + /// Provides a base class for implementations by implementing pinning logic for adaption. + /// + /// The element type. + internal abstract class ManagedBufferBase : MemoryManager + where T : struct + { + private GCHandle pinHandle; + + /// + public override unsafe MemoryHandle Pin(int elementIndex = 0) + { + if (!this.pinHandle.IsAllocated) + { + this.pinHandle = GCHandle.Alloc(this.GetPinnableObject(), GCHandleType.Pinned); + } + + void* ptr = (void*)this.pinHandle.AddrOfPinnedObject(); + return new MemoryHandle(ptr, this.pinHandle); + } + + /// + public override void Unpin() + { + if (this.pinHandle.IsAllocated) + { + this.pinHandle.Free(); + } + } + + /// + /// Gets the object that should be pinned. + /// + /// The pinnable . + protected abstract object GetPinnableObject(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs new file mode 100644 index 0000000000..20598c3e3a --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Memory managers are used to allocate memory for image processing operations. + /// + public abstract class MemoryAllocator + { + /// + /// Allocates an , holding a of length . + /// + /// Type of the data stored in the buffer. + /// Size of the buffer to allocate. + /// The allocation options. + /// A buffer of values of type . + public abstract IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + where T : struct; + + /// + /// Allocates an . + /// + /// The requested buffer length. + /// The allocation options. + /// The . + public abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None); + + /// + /// Releases all retained resources not being in use. + /// Eg: by resetting array pools and letting GC to free the arrays. + /// + public virtual void ReleaseRetainedResources() + { + } + } +} diff --git a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs new file mode 100644 index 0000000000..54b64b131f --- /dev/null +++ b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using SixLabors.ImageSharp.Memory.Internals; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Implements by newing up arrays by the GC on every allocation requests. + /// + public sealed class SimpleGcMemoryAllocator : MemoryAllocator + { + /// + public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) + { + Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); + + return new BasicArrayBuffer(new T[length]); + } + + /// + public override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None) + { + Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); + + return new BasicByteBuffer(new byte[length]); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 59247aa2d2..ba4f9c925d 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -6,20 +6,74 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Memory { /// /// Defines extension methods for . /// - internal static class Buffer2DExtensions + public static class Buffer2DExtensions { + /// + /// Gets a to the backing buffer of . + /// + /// The . + /// The value type. + /// The referencing the memory area. + public static Span GetSpan(this Buffer2D buffer) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.GetSpan(); + } + + /// + /// Gets the holding the backing buffer of . + /// + /// The . + /// The value type. + /// The . + public static Memory GetMemory(this Buffer2D buffer) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.Memory; + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The buffer + /// The y (row) coordinate + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int y) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The buffer + /// The y (row) coordinate + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory GetRowMemory(this Buffer2D buffer, int y) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); + } + /// /// Copy columns of inplace, /// from positions starting at to positions at . /// - public static unsafe void CopyColumns( + internal static unsafe void CopyColumns( this Buffer2D buffer, int sourceIndex, int destIndex, @@ -37,7 +91,7 @@ namespace SixLabors.ImageSharp.Memory int dOffset = destIndex * elementSize; long count = columnCount * elementSize; - Span span = MemoryMarshal.AsBytes(buffer.Memory.Span); + Span span = MemoryMarshal.AsBytes(buffer.GetMemory().Span); fixed (byte* ptr = span) { @@ -60,7 +114,7 @@ namespace SixLabors.ImageSharp.Memory /// The element type /// The /// The - public static Rectangle FullRectangle(this Buffer2D buffer) + internal static Rectangle FullRectangle(this Buffer2D buffer) where T : struct { return new Rectangle(0, 0, buffer.Width, buffer.Height); @@ -73,11 +127,11 @@ namespace SixLabors.ImageSharp.Memory /// The /// The rectangle subarea /// The - public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) + internal static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) where T : struct => new BufferArea(buffer, rectangle); - public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) + internal static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) where T : struct => new BufferArea(buffer, new Rectangle(x, y, width, height)); @@ -87,64 +141,17 @@ namespace SixLabors.ImageSharp.Memory /// The element type /// The /// The - public static BufferArea GetArea(this Buffer2D buffer) + internal static BufferArea GetArea(this Buffer2D buffer) where T : struct => new BufferArea(buffer); - public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) - where T : struct => - new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); - /// /// Gets a span for all the pixels in defined by /// - public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) - where T : struct - { - return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory GetRowMemory(this Buffer2D buffer, int y) - where T : struct - { - return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. - /// - /// The buffer - /// The x coordinate (position in the row) - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int x, int y) - where T : struct - { - return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int y) + internal static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) where T : struct { - return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + return buffer.GetSpan().Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); } /// @@ -153,21 +160,12 @@ namespace SixLabors.ImageSharp.Memory /// The element type /// The /// The of the buffer - public static Size Size(this Buffer2D buffer) + internal static Size Size(this Buffer2D buffer) where T : struct { return new Size(buffer.Width, buffer.Height); } - /// - /// Gets a to the backing buffer of . - /// - internal static Span GetSpan(this Buffer2D buffer) - where T : struct - { - return buffer.MemorySource.GetSpan(); - } - [Conditional("DEBUG")] private static void CheckColumnRegionsDoNotOverlap( Buffer2D buffer, diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 82a98bfc63..6b7f3bf42f 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -4,16 +4,18 @@ using System; 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. /// + /// + /// Before RC1, this class might be target of API changes, use it on your own risk! + /// /// The value type. - internal sealed class Buffer2D : IDisposable + // TODO: Consider moving this type to the SixLabors.ImageSharp.Memory namespace (SixLabors.Core). + public sealed class Buffer2D : IDisposable where T : struct { private MemorySource memorySource; @@ -24,7 +26,7 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(MemorySource memorySource, int width, int height) + internal Buffer2D(MemorySource memorySource, int width, int height) { this.memorySource = memorySource; this.Width = width; @@ -44,11 +46,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets the backing /// - public MemorySource MemorySource => this.memorySource; - - public Memory Memory => this.MemorySource.Memory; - - public Span Span => this.Memory.Span; + internal MemorySource MemorySource => this.memorySource; /// /// Gets a reference to the element at the specified position. @@ -56,7 +54,7 @@ namespace SixLabors.ImageSharp.Memory /// The x coordinate (row) /// The y coordinate (position at row) /// A reference to the element. - public ref T this[int x, int y] + internal ref T this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -64,28 +62,11 @@ namespace SixLabors.ImageSharp.Memory DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - Span span = this.Span; + Span span = this.GetSpan(); return ref span[(this.Width * y) + x]; } } - /// - /// Creates a new instance that maps to a target rows interval from the current instance. - /// - /// The target vertical offset for the rows interval to retrieve. - /// The desired number of rows to extract. - /// The new instance with the requested rows interval. - public Buffer2D Slice(int y, int h) - { - DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y)); - DebugGuard.MustBeGreaterThan(h, 0, nameof(h)); - DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h)); - - Memory slice = this.Memory.Slice(y * this.Width, h * this.Width); - var memory = new MemorySource(slice); - return new Buffer2D(memory, this.Width, h); - } - /// /// Disposes the instance /// @@ -98,7 +79,7 @@ namespace SixLabors.ImageSharp.Memory /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! /// - public static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) + internal static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) { MemorySource.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource); SwapDimensionData(destination, source); diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index 38f0b8129d..08731846e8 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -3,8 +3,6 @@ using System; using System.Runtime.CompilerServices; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Memory { /// diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index b596351b5f..6e317bb8f6 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -1,18 +1,25 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Buffers; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Memory { /// /// Extension methods for . /// - internal static class MemoryAllocatorExtensions + public static class MemoryAllocatorExtensions { + /// + /// Allocates a buffer of value type objects interpreted as a 2D region + /// of x elements. + /// + /// The type of buffer items to allocate. + /// The memory allocator. + /// The buffer width. + /// The buffer heght. + /// The allocation options. + /// The . public static Buffer2D Allocate2D( this MemoryAllocator memoryAllocator, int width, @@ -26,6 +33,15 @@ namespace SixLabors.ImageSharp.Memory return new Buffer2D(memorySource, width, height); } + /// + /// Allocates a buffer of value type objects interpreted as a 2D region + /// of width x height elements. + /// + /// The type of buffer items to allocate. + /// The memory allocator. + /// The buffer size. + /// The allocation options. + /// The . public static Buffer2D Allocate2D( this MemoryAllocator memoryAllocator, Size size, @@ -41,7 +57,7 @@ namespace SixLabors.ImageSharp.Memory /// The pixel size in bytes, eg. 3 for RGB /// The padding /// A - public static IManagedByteBuffer AllocatePaddedPixelRowBuffer( + internal static IManagedByteBuffer AllocatePaddedPixelRowBuffer( this MemoryAllocator memoryAllocator, int width, int pixelSizeInBytes, @@ -51,4 +67,4 @@ namespace SixLabors.ImageSharp.Memory return memoryAllocator.AllocateManagedByteBuffer(length); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Memory/MemorySource.cs b/src/ImageSharp/Memory/MemorySource.cs index f0b0ab0281..54f1bb0d1f 100644 --- a/src/ImageSharp/Memory/MemorySource.cs +++ b/src/ImageSharp/Memory/MemorySource.cs @@ -4,8 +4,6 @@ using System; using System.Buffers; -using SixLabors.Memory; - namespace SixLabors.ImageSharp.Memory { /// diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 815918754a..c2962cfe97 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -1,35 +1,38 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Memory { /// /// Represents an interval of rows in a and/or /// - internal readonly struct RowInterval : IEquatable + /// + /// Before RC1, this class might be target of API changes, use it on your own risk! + /// + public readonly struct RowInterval : IEquatable { /// /// Initializes a new instance of the struct. /// + /// The inclusive minimum row. + /// The exclusive maximum row. public RowInterval(int min, int max) { - DebugGuard.MustBeLessThan(min, max, nameof(min)); + Guard.MustBeLessThan(min, max, nameof(min)); this.Min = min; this.Max = max; } /// - /// Gets the INCLUSIVE minimum. + /// Gets the inclusive minimum row. /// public int Min { get; } /// - /// Gets the EXCLUSIVE maximum. + /// Gets the exclusive maximum row. /// public int Max { get; } @@ -38,33 +41,48 @@ namespace SixLabors.ImageSharp.Memory /// public int Height => this.Max - this.Min; + /// + /// Returns a boolean indicating whether the given two -s are equal. + /// + /// The first to compare. + /// The second to compare. + /// True if the given -s are equal; False otherwise. public static bool operator ==(RowInterval left, RowInterval right) { return left.Equals(right); } + /// + /// Returns a boolean indicating whether the given two -s are not equal. + /// + /// The first to compare. + /// The second to compare. + /// True if the given -s are not equal; False otherwise. public static bool operator !=(RowInterval left, RowInterval right) { return !left.Equals(right); } /// - public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; - - public RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); - - public RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); - public bool Equals(RowInterval other) { return this.Min == other.Min && this.Max == other.Max; } + /// public override bool Equals(object obj) { return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other); } + /// public override int GetHashCode() => HashCode.Combine(this.Min, this.Max); + + /// + public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; + + internal RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); + + internal RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs deleted file mode 100644 index 0ed3a43b70..0000000000 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using static SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag; - -namespace SixLabors.ImageSharp.Metadata.Profiles.Exif -{ - internal static class ExifTags - { - /// - /// The collection if Image File Directory tags - /// - public static readonly ExifTag[] Ifd = - { - SubfileType, - OldSubfileType, - ImageWidth, - ImageLength, - BitsPerSample, - Compression, - PhotometricInterpretation, - Thresholding, - CellWidth, - CellLength, - FillOrder, - DocumentName, - ImageDescription, - Make, - Model, - StripOffsets, - Orientation, - SamplesPerPixel, - RowsPerStrip, - StripByteCounts, - MinSampleValue, - MaxSampleValue, - XResolution, - YResolution, - PlanarConfiguration, - PageName, - XPosition, - YPosition, - FreeOffsets, - FreeByteCounts, - GrayResponseUnit, - GrayResponseCurve, - T4Options, - T6Options, - ResolutionUnit, - PageNumber, - ColorResponseUnit, - TransferFunction, - Software, - DateTime, - Artist, - HostComputer, - Predictor, - WhitePoint, - PrimaryChromaticities, - ColorMap, - HalftoneHints, - TileWidth, - TileLength, - TileOffsets, - TileByteCounts, - BadFaxLines, - CleanFaxData, - ConsecutiveBadFaxLines, - InkSet, - InkNames, - NumberOfInks, - DotRange, - TargetPrinter, - ExtraSamples, - SampleFormat, - SMinSampleValue, - SMaxSampleValue, - TransferRange, - ClipPath, - XClipPathUnits, - YClipPathUnits, - Indexed, - JPEGTables, - OPIProxy, - ProfileType, - FaxProfile, - CodingMethods, - VersionYear, - ModeNumber, - Decode, - DefaultImageColor, - T82ptions, - JPEGProc, - JPEGInterchangeFormat, - JPEGInterchangeFormatLength, - JPEGRestartInterval, - JPEGLosslessPredictors, - JPEGPointTransforms, - JPEGQTables, - JPEGDCTables, - JPEGACTables, - YCbCrCoefficients, - YCbCrSubsampling, - YCbCrSubsampling, - YCbCrPositioning, - ReferenceBlackWhite, - StripRowCounts, - XMP, - Rating, - RatingPercent, - ImageID, - CFARepeatPatternDim, - CFAPattern2, - BatteryLevel, - Copyright, - MDFileTag, - MDScalePixel, - MDLabName, - MDSampleInfo, - MDPrepDate, - MDPrepTime, - MDFileUnits, - PixelScale, - IntergraphPacketData, - IntergraphRegisters, - IntergraphMatrix, - ModelTiePoint, - SEMInfo, - ModelTransform, - ImageLayer, - FaxRecvParams, - FaxSubaddress, - FaxRecvTime, - ImageSourceData, - XPTitle, - XPComment, - XPAuthor, - XPKeywords, - XPSubject, - GDALMetadata, - GDALNoData - }; - - /// - /// The collection of Exif tags - /// - public static readonly ExifTag[] Exif = - { - ExposureTime, - FNumber, - ExposureProgram, - SpectralSensitivity, - ISOSpeedRatings, - OECF, - Interlace, - TimeZoneOffset, - SelfTimerMode, - SensitivityType, - StandardOutputSensitivity, - RecommendedExposureIndex, - ISOSpeed, - ISOSpeedLatitudeyyy, - ISOSpeedLatitudezzz, - ExifVersion, - DateTimeOriginal, - DateTimeDigitized, - OffsetTime, - OffsetTimeOriginal, - OffsetTimeDigitized, - ComponentsConfiguration, - CompressedBitsPerPixel, - ShutterSpeedValue, - ApertureValue, - BrightnessValue, - ExposureBiasValue, - MaxApertureValue, - SubjectDistance, - MeteringMode, - LightSource, - Flash, - FocalLength, - FlashEnergy2, - SpatialFrequencyResponse2, - Noise, - FocalPlaneXResolution2, - FocalPlaneYResolution2, - FocalPlaneResolutionUnit2, - ImageNumber, - SecurityClassification, - ImageHistory, - SubjectArea, - ExposureIndex2, - TIFFEPStandardID, - SensingMethod2, - MakerNote, - UserComment, - SubsecTime, - SubsecTimeOriginal, - SubsecTimeDigitized, - AmbientTemperature, - Humidity, - Pressure, - WaterDepth, - Acceleration, - CameraElevationAngle, - FlashpixVersion, - ColorSpace, - PixelXDimension, - PixelYDimension, - RelatedSoundFile, - FlashEnergy, - SpatialFrequencyResponse, - FocalPlaneXResolution, - FocalPlaneYResolution, - FocalPlaneResolutionUnit, - SubjectLocation, - ExposureIndex, - SensingMethod, - FileSource, - SceneType, - CFAPattern, - CustomRendered, - ExposureMode, - WhiteBalance, - DigitalZoomRatio, - FocalLengthIn35mmFilm, - SceneCaptureType, - GainControl, - Contrast, - Saturation, - Sharpness, - DeviceSettingDescription, - SubjectDistanceRange, - ImageUniqueID, - OwnerName, - SerialNumber, - LensInfo, - LensMake, - LensModel, - LensSerialNumber - }; - - /// - /// The collection of GPS tags - /// - public static readonly ExifTag[] Gps = - { - GPSVersionID, - GPSLatitudeRef, - GPSLatitude, - GPSLongitudeRef, - GPSLongitude, - GPSAltitudeRef, - GPSAltitude, - GPSTimestamp, - GPSSatellites, - GPSStatus, - GPSMeasureMode, - GPSDOP, - GPSSpeedRef, - GPSSpeed, - GPSTrackRef, - GPSTrack, - GPSImgDirectionRef, - GPSImgDirection, - GPSMapDatum, - GPSDestLatitudeRef, - GPSDestLatitude, - GPSDestLongitudeRef, - GPSDestLongitude, - GPSDestBearingRef, - GPSDestBearing, - GPSDestDistanceRef, - GPSDestDistance, - GPSProcessingMethod, - GPSAreaInformation, - GPSDateStamp, - GPSDifferential - }; - } -} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs deleted file mode 100644 index 05a9f35c9b..0000000000 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Globalization; -using System.Text; -using SixLabors.ImageSharp.Primitives; - -namespace SixLabors.ImageSharp.Metadata.Profiles.Exif -{ - /// - /// Represent the value of the EXIF profile. - /// - public sealed class ExifValue : IEquatable, IDeepCloneable - { - /// - /// Initializes a new instance of the class. - /// - /// The tag. - /// The data type. - /// The value. - /// Whether the value is an array. - internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray) - { - this.Tag = tag; - this.DataType = dataType; - this.IsArray = isArray && dataType != ExifDataType.Ascii; - this.Value = value; - } - - /// - /// Initializes a new instance of the class - /// by making a copy from another exif value. - /// - /// The other exif value, where the clone should be made from. - /// is null. - private ExifValue(ExifValue other) - { - Guard.NotNull(other, nameof(other)); - - this.DataType = other.DataType; - this.IsArray = other.IsArray; - this.Tag = other.Tag; - - if (!other.IsArray) - { - // All types are value types except for string which is immutable so safe to simply assign. - this.Value = other.Value; - } - else - { - // All array types are value types so Clone() is sufficient here. - var array = (Array)other.Value; - this.Value = array.Clone(); - } - } - - /// - /// Gets the data type of the exif value. - /// - public ExifDataType DataType { get; } - - /// - /// Gets a value indicating whether the value is an array. - /// - public bool IsArray { get; } - - /// - /// Gets the tag of the exif value. - /// - public ExifTag Tag { get; } - - /// - /// Gets the value. - /// - public object Value { get; } - - /// - /// Gets a value indicating whether the EXIF value has a value. - /// - internal bool HasValue - { - get - { - if (this.Value is null) - { - return false; - } - - if (this.DataType == ExifDataType.Ascii) - { - return ((string)this.Value).Length > 0; - } - - return true; - } - } - - /// - /// Gets the length of the EXIF value - /// - internal int Length - { - get - { - if (this.Value is null) - { - return 4; - } - - int size = (int)(GetSize(this.DataType) * this.NumberOfComponents); - - return size < 4 ? 4 : size; - } - } - - /// - /// Gets the number of components. - /// - internal int NumberOfComponents - { - get - { - if (this.DataType == ExifDataType.Ascii) - { - return Encoding.UTF8.GetBytes((string)this.Value).Length; - } - - if (this.IsArray) - { - return ((Array)this.Value).Length; - } - - return 1; - } - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - public static bool operator ==(ExifValue left, ExifValue right) => ReferenceEquals(left, right) || left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - public static bool operator !=(ExifValue left, ExifValue right) => !(left == right); - - /// - public override bool Equals(object obj) => obj is ExifValue other && this.Equals(other); - - /// - public bool Equals(ExifValue other) - { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return - this.Tag == other.Tag - && this.DataType == other.DataType - && object.Equals(this.Value, other.Value); - } - - /// - /// Clones the current value, overwriting the value. - /// - /// The value to overwrite. - /// - public ExifValue WithValue(object value) - { - this.CheckValue(value); - - return new ExifValue(this.Tag, this.DataType, value, this.IsArray); - } - - /// - public override int GetHashCode() - { - return HashCode.Combine(this.Tag, this.DataType, this.Value); - } - - /// - public override string ToString() - { - if (this.Value is null) - { - return null; - } - - if (this.DataType == ExifDataType.Ascii) - { - return (string)this.Value; - } - - if (!this.IsArray) - { - return this.ToString(this.Value); - } - - var sb = new StringBuilder(); - foreach (object value in (Array)this.Value) - { - sb.Append(this.ToString(value)); - sb.Append(' '); - } - - return sb.ToString(); - } - - /// - public ExifValue DeepClone() => new ExifValue(this); - - /// - /// Creates a new - /// - /// The tag. - /// The value. - /// - /// The . - /// - /// - /// Thrown if the tag is not supported. - /// - internal static ExifValue Create(ExifTag tag, object value) - { - Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag"); - - switch (tag) - { - case ExifTag.ImageDescription: - case ExifTag.Make: - case ExifTag.Model: - case ExifTag.Software: - case ExifTag.DateTime: - case ExifTag.Artist: - case ExifTag.HostComputer: - case ExifTag.Copyright: - case ExifTag.DocumentName: - case ExifTag.PageName: - case ExifTag.InkNames: - case ExifTag.TargetPrinter: - case ExifTag.ImageID: - case ExifTag.MDLabName: - case ExifTag.MDSampleInfo: - case ExifTag.MDPrepDate: - case ExifTag.MDPrepTime: - case ExifTag.MDFileUnits: - case ExifTag.SEMInfo: - case ExifTag.SpectralSensitivity: - case ExifTag.DateTimeOriginal: - case ExifTag.DateTimeDigitized: - case ExifTag.SubsecTime: - case ExifTag.SubsecTimeOriginal: - case ExifTag.SubsecTimeDigitized: - case ExifTag.FaxSubaddress: - case ExifTag.OffsetTime: - case ExifTag.OffsetTimeOriginal: - case ExifTag.OffsetTimeDigitized: - case ExifTag.SecurityClassification: - case ExifTag.ImageHistory: - case ExifTag.ImageUniqueID: - case ExifTag.OwnerName: - case ExifTag.SerialNumber: - case ExifTag.LensMake: - case ExifTag.LensModel: - case ExifTag.LensSerialNumber: - case ExifTag.GDALMetadata: - case ExifTag.GDALNoData: - case ExifTag.GPSLatitudeRef: - case ExifTag.GPSLongitudeRef: - case ExifTag.GPSSatellites: - case ExifTag.GPSStatus: - case ExifTag.GPSMeasureMode: - case ExifTag.GPSSpeedRef: - case ExifTag.GPSTrackRef: - case ExifTag.GPSImgDirectionRef: - case ExifTag.GPSMapDatum: - case ExifTag.GPSDestLatitudeRef: - case ExifTag.GPSDestLongitudeRef: - case ExifTag.GPSDestBearingRef: - case ExifTag.GPSDestDistanceRef: - case ExifTag.GPSDateStamp: - return new ExifValue(tag, ExifDataType.Ascii, value, true); - - case ExifTag.ClipPath: - case ExifTag.VersionYear: - case ExifTag.XMP: - case ExifTag.CFAPattern2: - case ExifTag.TIFFEPStandardID: - case ExifTag.XPTitle: - case ExifTag.XPComment: - case ExifTag.XPAuthor: - case ExifTag.XPKeywords: - case ExifTag.XPSubject: - case ExifTag.GPSVersionID: - return new ExifValue(tag, ExifDataType.Byte, value, true); - - case ExifTag.FaxProfile: - case ExifTag.ModeNumber: - case ExifTag.GPSAltitudeRef: - return new ExifValue(tag, ExifDataType.Byte, value, false); - - case ExifTag.FreeOffsets: - case ExifTag.FreeByteCounts: - case ExifTag.ColorResponseUnit: - case ExifTag.TileOffsets: - case ExifTag.SMinSampleValue: - case ExifTag.SMaxSampleValue: - case ExifTag.JPEGQTables: - case ExifTag.JPEGDCTables: - case ExifTag.JPEGACTables: - case ExifTag.StripRowCounts: - case ExifTag.IntergraphRegisters: - case ExifTag.TimeZoneOffset: - return new ExifValue(tag, ExifDataType.Long, value, true); - case ExifTag.SubfileType: - case ExifTag.SubIFDOffset: - case ExifTag.GPSIFDOffset: - case ExifTag.T4Options: - case ExifTag.T6Options: - case ExifTag.XClipPathUnits: - case ExifTag.YClipPathUnits: - case ExifTag.ProfileType: - case ExifTag.CodingMethods: - case ExifTag.T82ptions: - case ExifTag.JPEGInterchangeFormat: - case ExifTag.JPEGInterchangeFormatLength: - case ExifTag.MDFileTag: - case ExifTag.StandardOutputSensitivity: - case ExifTag.RecommendedExposureIndex: - case ExifTag.ISOSpeed: - case ExifTag.ISOSpeedLatitudeyyy: - case ExifTag.ISOSpeedLatitudezzz: - case ExifTag.FaxRecvParams: - case ExifTag.FaxRecvTime: - case ExifTag.ImageNumber: - return new ExifValue(tag, ExifDataType.Long, value, false); - - case ExifTag.WhitePoint: - case ExifTag.PrimaryChromaticities: - case ExifTag.YCbCrCoefficients: - case ExifTag.ReferenceBlackWhite: - case ExifTag.PixelScale: - case ExifTag.IntergraphMatrix: - case ExifTag.ModelTiePoint: - case ExifTag.ModelTransform: - case ExifTag.GPSLatitude: - case ExifTag.GPSLongitude: - case ExifTag.GPSTimestamp: - case ExifTag.GPSDestLatitude: - case ExifTag.GPSDestLongitude: - return new ExifValue(tag, ExifDataType.Rational, value, true); - - case ExifTag.XPosition: - case ExifTag.YPosition: - case ExifTag.XResolution: - case ExifTag.YResolution: - case ExifTag.BatteryLevel: - case ExifTag.ExposureTime: - case ExifTag.FNumber: - case ExifTag.MDScalePixel: - case ExifTag.CompressedBitsPerPixel: - case ExifTag.ApertureValue: - case ExifTag.MaxApertureValue: - case ExifTag.SubjectDistance: - case ExifTag.FocalLength: - case ExifTag.FlashEnergy2: - case ExifTag.FocalPlaneXResolution2: - case ExifTag.FocalPlaneYResolution2: - case ExifTag.ExposureIndex2: - case ExifTag.Humidity: - case ExifTag.Pressure: - case ExifTag.Acceleration: - case ExifTag.FlashEnergy: - case ExifTag.FocalPlaneXResolution: - case ExifTag.FocalPlaneYResolution: - case ExifTag.ExposureIndex: - case ExifTag.DigitalZoomRatio: - case ExifTag.LensInfo: - case ExifTag.GPSAltitude: - case ExifTag.GPSDOP: - case ExifTag.GPSSpeed: - case ExifTag.GPSTrack: - case ExifTag.GPSImgDirection: - case ExifTag.GPSDestBearing: - case ExifTag.GPSDestDistance: - return new ExifValue(tag, ExifDataType.Rational, value, false); - - case ExifTag.BitsPerSample: - case ExifTag.MinSampleValue: - case ExifTag.MaxSampleValue: - case ExifTag.GrayResponseCurve: - case ExifTag.ColorMap: - case ExifTag.ExtraSamples: - case ExifTag.PageNumber: - case ExifTag.TransferFunction: - case ExifTag.Predictor: - case ExifTag.HalftoneHints: - case ExifTag.SampleFormat: - case ExifTag.TransferRange: - case ExifTag.DefaultImageColor: - case ExifTag.JPEGLosslessPredictors: - case ExifTag.JPEGPointTransforms: - case ExifTag.YCbCrSubsampling: - case ExifTag.CFARepeatPatternDim: - case ExifTag.IntergraphPacketData: - case ExifTag.ISOSpeedRatings: - case ExifTag.SubjectArea: - case ExifTag.SubjectLocation: - return new ExifValue(tag, ExifDataType.Short, value, true); - - case ExifTag.OldSubfileType: - case ExifTag.Compression: - case ExifTag.PhotometricInterpretation: - case ExifTag.Thresholding: - case ExifTag.CellWidth: - case ExifTag.CellLength: - case ExifTag.FillOrder: - case ExifTag.Orientation: - case ExifTag.SamplesPerPixel: - case ExifTag.PlanarConfiguration: - case ExifTag.GrayResponseUnit: - case ExifTag.ResolutionUnit: - case ExifTag.CleanFaxData: - case ExifTag.InkSet: - case ExifTag.NumberOfInks: - case ExifTag.DotRange: - case ExifTag.Indexed: - case ExifTag.OPIProxy: - case ExifTag.JPEGProc: - case ExifTag.JPEGRestartInterval: - case ExifTag.YCbCrPositioning: - case ExifTag.Rating: - case ExifTag.RatingPercent: - case ExifTag.ExposureProgram: - case ExifTag.Interlace: - case ExifTag.SelfTimerMode: - case ExifTag.SensitivityType: - case ExifTag.MeteringMode: - case ExifTag.LightSource: - case ExifTag.FocalPlaneResolutionUnit2: - case ExifTag.SensingMethod2: - case ExifTag.Flash: - case ExifTag.ColorSpace: - case ExifTag.FocalPlaneResolutionUnit: - case ExifTag.SensingMethod: - case ExifTag.CustomRendered: - case ExifTag.ExposureMode: - case ExifTag.WhiteBalance: - case ExifTag.FocalLengthIn35mmFilm: - case ExifTag.SceneCaptureType: - case ExifTag.GainControl: - case ExifTag.Contrast: - case ExifTag.Saturation: - case ExifTag.Sharpness: - case ExifTag.SubjectDistanceRange: - case ExifTag.GPSDifferential: - return new ExifValue(tag, ExifDataType.Short, value, false); - - case ExifTag.Decode: - return new ExifValue(tag, ExifDataType.SignedRational, value, true); - - case ExifTag.ShutterSpeedValue: - case ExifTag.BrightnessValue: - case ExifTag.ExposureBiasValue: - case ExifTag.AmbientTemperature: - case ExifTag.WaterDepth: - case ExifTag.CameraElevationAngle: - return new ExifValue(tag, ExifDataType.SignedRational, value, false); - - case ExifTag.JPEGTables: - case ExifTag.OECF: - case ExifTag.ExifVersion: - case ExifTag.ComponentsConfiguration: - case ExifTag.MakerNote: - case ExifTag.UserComment: - case ExifTag.FlashpixVersion: - case ExifTag.SpatialFrequencyResponse: - case ExifTag.SpatialFrequencyResponse2: - case ExifTag.Noise: - case ExifTag.CFAPattern: - case ExifTag.DeviceSettingDescription: - case ExifTag.ImageSourceData: - case ExifTag.GPSProcessingMethod: - case ExifTag.GPSAreaInformation: - return new ExifValue(tag, ExifDataType.Undefined, value, true); - - case ExifTag.FileSource: - case ExifTag.SceneType: - return new ExifValue(tag, ExifDataType.Undefined, value, false); - - case ExifTag.StripOffsets: - case ExifTag.TileByteCounts: - case ExifTag.ImageLayer: - return CreateNumber(tag, value, true); - - case ExifTag.ImageWidth: - case ExifTag.ImageLength: - case ExifTag.TileWidth: - case ExifTag.TileLength: - case ExifTag.BadFaxLines: - case ExifTag.ConsecutiveBadFaxLines: - case ExifTag.PixelXDimension: - case ExifTag.PixelYDimension: - return CreateNumber(tag, value, false); - - default: - throw new NotSupportedException(); - } - } - - /// - /// Gets the size in bytes of the given data type. - /// - /// The data type. - /// - /// The . - /// - /// - /// Thrown if the type is unsupported. - /// - internal static uint GetSize(ExifDataType dataType) - { - switch (dataType) - { - case ExifDataType.Ascii: - case ExifDataType.Byte: - case ExifDataType.SignedByte: - case ExifDataType.Undefined: - return 1; - case ExifDataType.Short: - case ExifDataType.SignedShort: - return 2; - case ExifDataType.Long: - case ExifDataType.SignedLong: - case ExifDataType.SingleFloat: - return 4; - case ExifDataType.DoubleFloat: - case ExifDataType.Rational: - case ExifDataType.SignedRational: - return 8; - default: - throw new NotSupportedException(dataType.ToString()); - } - } - - /// - /// Returns an EXIF value with a numeric type for the given tag. - /// - /// The tag. - /// The value. - /// Whether the value is an array. - /// - /// The . - /// - private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray) - { - Type type = value?.GetType(); - if (type?.IsArray == true) - { - type = type.GetElementType(); - } - - if (type is null || type == typeof(ushort)) - { - return new ExifValue(tag, ExifDataType.Short, value, isArray); - } - - if (type == typeof(short)) - { - return new ExifValue(tag, ExifDataType.SignedShort, value, isArray); - } - - if (type == typeof(uint)) - { - return new ExifValue(tag, ExifDataType.Long, value, isArray); - } - - return new ExifValue(tag, ExifDataType.SignedLong, value, isArray); - } - - /// - /// Checks the value type of the given object. - /// - /// The value to check. - /// - /// Thrown if the object type is not supported. - /// - private void CheckValue(object value) - { - if (value is null) - { - return; - } - - Type type = value.GetType(); - - if (this.DataType == ExifDataType.Ascii) - { - Guard.IsTrue(type == typeof(string), nameof(value), "Value should be a string."); - return; - } - - if (type.IsArray) - { - Guard.IsTrue(this.IsArray, nameof(value), "Value should not be an array."); - type = type.GetElementType(); - } - else - { - Guard.IsFalse(this.IsArray, nameof(value), "Value should not be an array."); - } - - switch (this.DataType) - { - case ExifDataType.Byte: - Guard.IsTrue(type == typeof(byte), nameof(value), $"Value should be a byte{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.DoubleFloat: - Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Long: - Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Rational: - Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Short: - Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedByte: - Guard.IsTrue(type == typeof(sbyte), nameof(value), $"Value should be a signed byte{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedLong: - Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedRational: - Guard.IsTrue(type == typeof(SignedRational), nameof(value), $"Value should be a SignedRational{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SignedShort: - Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.SingleFloat: - Guard.IsTrue(type == typeof(float), nameof(value), $"Value should be a float{(this.IsArray ? " array." : ".")}"); - break; - case ExifDataType.Undefined: - Guard.IsTrue(type == typeof(byte), nameof(value), "Value should be a byte array."); - break; - default: - throw new NotSupportedException(); - } - } - - /// - /// Converts the object value of this instance to its equivalent string representation - /// - /// The value - /// The - private string ToString(object value) - { - if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description) - { - return description; - } - - switch (this.DataType) - { - case ExifDataType.Ascii: - return (string)value; - case ExifDataType.Byte: - return ((byte)value).ToString("X2", CultureInfo.InvariantCulture); - case ExifDataType.DoubleFloat: - return ((double)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Long: - return ((uint)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Rational: - return ((Rational)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Short: - return ((ushort)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SignedByte: - return ((sbyte)value).ToString("X2", CultureInfo.InvariantCulture); - case ExifDataType.SignedLong: - return ((int)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SignedRational: - return ((Rational)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SignedShort: - return ((short)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.SingleFloat: - return ((float)value).ToString(CultureInfo.InvariantCulture); - case ExifDataType.Undefined: - return ((byte)value).ToString("X2", CultureInfo.InvariantCulture); - default: - throw new NotSupportedException(); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/README.md b/src/ImageSharp/MetaData/Profiles/Exif/README.md deleted file mode 100644 index b6e27b70c5..0000000000 --- a/src/ImageSharp/MetaData/Profiles/Exif/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Adapted from Magick.NET: - -https://github.com/dlemstra/Magick.NET/tree/784e23b1f5c824fc03d4b95d3387b3efe1ed510b/Magick.NET/Core/Profiles/Exif \ No newline at end of file diff --git a/src/ImageSharp/MetaData/FrameDecodingMode.cs b/src/ImageSharp/Metadata/FrameDecodingMode.cs similarity index 100% rename from src/ImageSharp/MetaData/FrameDecodingMode.cs rename to src/ImageSharp/Metadata/FrameDecodingMode.cs diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs similarity index 100% rename from src/ImageSharp/MetaData/ImageFrameMetaData.cs rename to src/ImageSharp/Metadata/ImageFrameMetadata.cs diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/Metadata/ImageMetadata.cs similarity index 100% rename from src/ImageSharp/MetaData/ImageMetaData.cs rename to src/ImageSharp/Metadata/ImageMetadata.cs diff --git a/src/ImageSharp/MetaData/PixelResolutionUnit.cs b/src/ImageSharp/Metadata/PixelResolutionUnit.cs similarity index 100% rename from src/ImageSharp/MetaData/PixelResolutionUnit.cs rename to src/ImageSharp/Metadata/PixelResolutionUnit.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf new file mode 100644 index 0000000000..9be0c8402b Binary files /dev/null and b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf differ diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs similarity index 83% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs index 83e7f7fe8b..74c86f7214 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Metadata.Profiles.Exif @@ -20,6 +20,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL. + /// + /// Although the standard defines ASCII this has commonly been ignored as + /// ASCII cannot properly encode text in many languages. + /// /// Ascii = 2, @@ -64,13 +68,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif SignedRational = 10, /// - /// A 32-bit floating point value. + /// A 32-bit single precision floating point value. /// SingleFloat = 11, /// - /// A 64-bit floating point value. + /// A 64-bit double precision floating point value. /// DoubleFloat = 12 } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs new file mode 100644 index 0000000000..d94dc56401 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal static class ExifDataTypes + { + /// + /// Gets the size in bytes of the given data type. + /// + /// The data type. + /// + /// The . + /// + /// + /// Thrown if the type is unsupported. + /// + public static uint GetSize(ExifDataType dataType) + { + switch (dataType) + { + case ExifDataType.Ascii: + case ExifDataType.Byte: + case ExifDataType.SignedByte: + case ExifDataType.Undefined: + return 1; + case ExifDataType.Short: + case ExifDataType.SignedShort: + return 2; + case ExifDataType.Long: + case ExifDataType.SignedLong: + case ExifDataType.SingleFloat: + return 4; + case ExifDataType.DoubleFloat: + case ExifDataType.Rational: + case ExifDataType.SignedRational: + return 8; + default: + throw new NotSupportedException(dataType.ToString()); + } + } + } +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs similarity index 85% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs index d22dc730f9..6405a7ff2b 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -29,11 +29,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// GPSTags /// - GPSTags = 8, + GpsTags = 8, /// /// All /// - All = IfdTags | ExifTags | GPSTags + All = IfdTags | ExifTags | GpsTags } -} \ No newline at end of file +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs similarity index 78% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index 3d90cb3a9e..c2a731825a 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -1,11 +1,10 @@ -// Copyright (c) Six Labors 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 SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { @@ -22,7 +21,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The collection of EXIF values /// - private List values; + private List values; /// /// The thumbnail offset position in the byte stream @@ -70,9 +69,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (other.values != null) { - this.values = new List(other.Values.Count); + this.values = new List(other.Values.Count); - foreach (ExifValue value in other.Values) + foreach (IExifValue value in other.Values) { this.values.Add(value.DeepClone()); } @@ -98,7 +97,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Gets the values of this EXIF profile. /// - public IReadOnlyList Values + public IReadOnlyList Values { get { @@ -138,46 +137,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Returns the value with the specified tag. /// - /// The tag of the EXIF value. - /// - /// The . - /// - public ExifValue GetValue(ExifTag tag) - { - foreach (ExifValue exifValue in this.Values) - { - if (exifValue.Tag == tag) - { - return exifValue; - } - } - - return null; - } - - /// - /// Conditionally returns the value of the tag if it exists. - /// - /// The tag of the EXIF value. - /// The value of the tag, if found. - /// - /// The . - /// - public bool TryGetValue(ExifTag tag, out ExifValue value) + /// The tag of the exif value. + /// The value with the specified tag. + /// The data type of the tag. + public IExifValue GetValue(ExifTag tag) { - foreach (ExifValue exifValue in this.Values) - { - if (exifValue.Tag == tag) - { - value = exifValue; - - return true; - } - } - - value = default; - - return false; + IExifValue value = this.GetValueInternal(tag); + return value is null ? null : (IExifValue)value; } /// @@ -206,24 +172,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// Sets the value of the specified tag. /// - /// The tag of the EXIF value. + /// The tag of the exif value. /// The value. - public void SetValue(ExifTag tag, object value) - { - for (int i = 0; i < this.Values.Count; i++) - { - if (this.values[i].Tag == tag) - { - this.values[i] = this.values[i].WithValue(value); - - return; - } - } - - var newExifValue = ExifValue.Create(tag, value); - - this.values.Add(newExifValue); - } + /// The data type of the tag. + public void SetValue(ExifTag tag, TValueType value) + => this.SetValueInternal(tag, value); /// /// Converts this instance to a byte array. @@ -238,7 +191,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.values.Count == 0) { - return null; + return Array.Empty(); } var writer = new ExifWriter(this.values, this.Parts); @@ -248,6 +201,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public ExifProfile DeepClone() => new ExifProfile(this); + /// + /// Returns the value with the specified tag. + /// + /// The tag of the exif value. + /// The value with the specified tag. + internal IExifValue GetValueInternal(ExifTag tag) + { + foreach (IExifValue exifValue in this.Values) + { + if (exifValue.Tag == tag) + { + return exifValue; + } + } + + return null; + } + + /// + /// Sets the value of the specified tag. + /// + /// The tag of the exif value. + /// The value. + internal void SetValueInternal(ExifTag tag, object value) + { + foreach (IExifValue exifValue in this.Values) + { + if (exifValue.Tag == tag) + { + exifValue.TrySetValue(value); + return; + } + } + + ExifValue newExifValue = ExifValues.Create(tag); + if (newExifValue is null) + { + throw new NotSupportedException(); + } + + newExifValue.TrySetValue(value); + this.values.Add(newExifValue); + } + /// /// Synchronizes the profiles with the specified metadata. /// @@ -258,9 +255,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution); } - private void SyncResolution(ExifTag tag, double resolution) + private void SyncResolution(ExifTag tag, double resolution) { - ExifValue value = this.GetValue(tag); + IExifValue value = this.GetValue(tag); if (value is null) { @@ -285,7 +282,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.data is null) { - this.values = new List(); + this.values = new List(); return; } @@ -301,4 +298,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.thumbnailLength = (int)reader.ThumbnailLength; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs similarity index 82% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 77c1cf2eab..6ad8d24fa4 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -5,10 +5,8 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; using System.Runtime.CompilerServices; using System.Text; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { @@ -68,12 +66,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The . /// - public List ReadValues() + public List ReadValues() { - var values = new List(); + var values = new List(); // II == 0x4949 - this.isBigEndian = !(this.ReadUInt16() == 0x4949); + this.isBigEndian = this.ReadUInt16() != 0x4949; if (this.ReadUInt16() != 0x002A) { @@ -101,7 +99,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter) { - int dataTypeSize = (int)ExifValue.GetSize(dataType); + int dataTypeSize = (int)ExifDataTypes.GetSize(dataType); int length = data.Length / dataTypeSize; var result = new TDataType[length]; @@ -135,7 +133,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// The values. /// The index. - private void AddValues(List values, uint index) + private void AddValues(List values, uint index) { if (index > (uint)this.exifData.Length) { @@ -153,9 +151,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } bool duplicate = false; - foreach (ExifValue val in values) + foreach (IExifValue val in values) { - if (val.Tag == value.Tag) + if (val == value) { duplicate = true; break; @@ -167,19 +165,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif continue; } - if (value.Tag == ExifTag.SubIFDOffset) + if (value == ExifTag.SubIFDOffset) { - if (value.DataType == ExifDataType.Long) - { - this.exifOffset = (uint)value.Value; - } + this.exifOffset = ((ExifLong)value).Value; } - else if (value.Tag == ExifTag.GPSIFDOffset) + else if (value == ExifTag.GPSIFDOffset) { - if (value.DataType == ExifDataType.Long) - { - this.gpsOffset = (uint)value.Value; - } + this.gpsOffset = ((ExifLong)value).Value; } else { @@ -285,30 +277,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private bool TryReadValue(out ExifValue exifValue) { + exifValue = default; + // 2 | 2 | 4 | 4 // tag | type | count | value offset if (this.RemainingLength < 12) { - exifValue = default; - return false; } - ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown); - uint type = this.ReadUInt16(); + var tag = (ExifTagValue)this.ReadUInt16(); + ExifDataType dataType = EnumUtils.Parse(this.ReadUInt16(), ExifDataType.Unknown); // Ensure that the data type is valid - if (type == 0 || type > 12) + if (dataType == ExifDataType.Unknown) { - exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false); - - return true; + return false; } - var dataType = (ExifDataType)type; - - object value; - uint numberOfComponents = this.ReadUInt32(); // Issue #132: ExifDataType == Undefined is treated like a byte array. @@ -318,23 +304,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif numberOfComponents = 4; } - uint size = numberOfComponents * ExifValue.GetSize(dataType); + uint size = numberOfComponents * ExifDataTypes.GetSize(dataType); this.TryReadSpan(4, out ReadOnlySpan offsetBuffer); + object value; if (size > 4) { int oldIndex = this.position; - uint newIndex = this.ConvertToUInt32(offsetBuffer); // Ensure that the new index does not overrun the data if (newIndex > int.MaxValue) { - this.AddInvalidTag(tag); - - exifValue = default; - + this.AddInvalidTag(new UnkownExifTag(tag)); return false; } @@ -342,12 +325,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.RemainingLength < size) { - this.AddInvalidTag(tag); + this.AddInvalidTag(new UnkownExifTag(tag)); this.position = oldIndex; - - exifValue = default; - return false; } @@ -361,33 +341,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents); } - exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents != 1); - - return true; - } + exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents); - private void AddInvalidTag(ExifTag tag) - { - if (this.invalidTags is null) + if (exifValue is null) { - this.invalidTags = new List(); + this.AddInvalidTag(new UnkownExifTag(tag)); + return false; } - this.invalidTags.Add(tag); - } - - [MethodImpl(InliningOptions.ShortMethod)] - private TEnum ToEnum(int value, TEnum defaultValue) - where TEnum : struct, Enum - { - if (EnumHelper.IsDefined(value)) + if (!exifValue.TrySetValue(value)) { - return Unsafe.As(ref value); + return false; } - return defaultValue; + return true; } + private void AddInvalidTag(ExifTag tag) + => (this.invalidTags ?? (this.invalidTags = new List())).Add(tag); + private bool TryReadSpan(int length, out ReadOnlySpan span) { if (this.RemainingLength < length) @@ -421,18 +393,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private void GetThumbnail(uint offset) { - var values = new List(); + var values = new List(); this.AddValues(values, offset); foreach (ExifValue value in values) { - if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long)) + if (value == ExifTag.JPEGInterchangeFormat) { - this.ThumbnailOffset = (uint)value.Value; + this.ThumbnailOffset = ((ExifLong)value).Value; } - else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long) + else if (value == ExifTag.JPEGInterchangeFormatLength) { - this.ThumbnailLength = (uint)value.Value; + this.ThumbnailLength = ((ExifLong)value).Value; } } } @@ -541,18 +513,5 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif ? BinaryPrimitives.ReadInt16BigEndian(buffer) : BinaryPrimitives.ReadInt16LittleEndian(buffer); } - - private sealed class EnumHelper - where TEnum : struct, Enum - { - private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast() - .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray(); - - [MethodImpl(InliningOptions.ShortMethod)] - public static bool IsDefined(int value) - { - return Array.BinarySearch(Values, value) >= 0; - } - } } } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs similarity index 86% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs index 845e4ee734..b9bb2ee056 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -31,7 +31,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public static string GetDescription(ExifTag tag, object value) { - FieldInfo field = tag.GetType().GetTypeInfo().GetDeclaredField(tag.ToString()); + var tagValue = (ExifTagValue)(ushort)tag; + FieldInfo field = tagValue.GetType().GetTypeInfo().GetDeclaredField(tagValue.ToString()); if (field is null) { @@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { object attributeValue = customAttribute.ConstructorArguments[0].Value; - if (object.Equals(attributeValue, value)) + if (Equals(attributeValue, value)) { return (string)customAttribute.ConstructorArguments[1].Value; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs new file mode 100644 index 0000000000..a4123d02fe --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs @@ -0,0 +1,275 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal static class ExifTags + { + public static ExifParts GetPart(ExifTag tag) + { + switch ((ExifTagValue)(ushort)tag) + { + case ExifTagValue.SubfileType: + case ExifTagValue.OldSubfileType: + case ExifTagValue.ImageWidth: + case ExifTagValue.ImageLength: + case ExifTagValue.BitsPerSample: + case ExifTagValue.Compression: + case ExifTagValue.PhotometricInterpretation: + case ExifTagValue.Thresholding: + case ExifTagValue.CellWidth: + case ExifTagValue.CellLength: + case ExifTagValue.FillOrder: + case ExifTagValue.DocumentName: + case ExifTagValue.ImageDescription: + case ExifTagValue.Make: + case ExifTagValue.Model: + case ExifTagValue.StripOffsets: + case ExifTagValue.Orientation: + case ExifTagValue.SamplesPerPixel: + case ExifTagValue.RowsPerStrip: + case ExifTagValue.StripByteCounts: + case ExifTagValue.MinSampleValue: + case ExifTagValue.MaxSampleValue: + case ExifTagValue.XResolution: + case ExifTagValue.YResolution: + case ExifTagValue.PlanarConfiguration: + case ExifTagValue.PageName: + case ExifTagValue.XPosition: + case ExifTagValue.YPosition: + case ExifTagValue.FreeOffsets: + case ExifTagValue.FreeByteCounts: + case ExifTagValue.GrayResponseUnit: + case ExifTagValue.GrayResponseCurve: + case ExifTagValue.T4Options: + case ExifTagValue.T6Options: + case ExifTagValue.ResolutionUnit: + case ExifTagValue.PageNumber: + case ExifTagValue.ColorResponseUnit: + case ExifTagValue.TransferFunction: + case ExifTagValue.Software: + case ExifTagValue.DateTime: + case ExifTagValue.Artist: + case ExifTagValue.HostComputer: + case ExifTagValue.Predictor: + case ExifTagValue.WhitePoint: + case ExifTagValue.PrimaryChromaticities: + case ExifTagValue.ColorMap: + case ExifTagValue.HalftoneHints: + case ExifTagValue.TileWidth: + case ExifTagValue.TileLength: + case ExifTagValue.TileOffsets: + case ExifTagValue.TileByteCounts: + case ExifTagValue.BadFaxLines: + case ExifTagValue.CleanFaxData: + case ExifTagValue.ConsecutiveBadFaxLines: + case ExifTagValue.InkSet: + case ExifTagValue.InkNames: + case ExifTagValue.NumberOfInks: + case ExifTagValue.DotRange: + case ExifTagValue.TargetPrinter: + case ExifTagValue.ExtraSamples: + case ExifTagValue.SampleFormat: + case ExifTagValue.SMinSampleValue: + case ExifTagValue.SMaxSampleValue: + case ExifTagValue.TransferRange: + case ExifTagValue.ClipPath: + case ExifTagValue.XClipPathUnits: + case ExifTagValue.YClipPathUnits: + case ExifTagValue.Indexed: + case ExifTagValue.JPEGTables: + case ExifTagValue.OPIProxy: + case ExifTagValue.ProfileType: + case ExifTagValue.FaxProfile: + case ExifTagValue.CodingMethods: + case ExifTagValue.VersionYear: + case ExifTagValue.ModeNumber: + case ExifTagValue.Decode: + case ExifTagValue.DefaultImageColor: + case ExifTagValue.T82ptions: + case ExifTagValue.JPEGProc: + case ExifTagValue.JPEGInterchangeFormat: + case ExifTagValue.JPEGInterchangeFormatLength: + case ExifTagValue.JPEGRestartInterval: + case ExifTagValue.JPEGLosslessPredictors: + case ExifTagValue.JPEGPointTransforms: + case ExifTagValue.JPEGQTables: + case ExifTagValue.JPEGDCTables: + case ExifTagValue.JPEGACTables: + case ExifTagValue.YCbCrCoefficients: + case ExifTagValue.YCbCrPositioning: + case ExifTagValue.YCbCrSubsampling: + case ExifTagValue.ReferenceBlackWhite: + case ExifTagValue.StripRowCounts: + case ExifTagValue.XMP: + case ExifTagValue.Rating: + case ExifTagValue.RatingPercent: + case ExifTagValue.ImageID: + case ExifTagValue.CFARepeatPatternDim: + case ExifTagValue.CFAPattern2: + case ExifTagValue.BatteryLevel: + case ExifTagValue.Copyright: + case ExifTagValue.MDFileTag: + case ExifTagValue.MDScalePixel: + case ExifTagValue.MDLabName: + case ExifTagValue.MDSampleInfo: + case ExifTagValue.MDPrepDate: + case ExifTagValue.MDPrepTime: + case ExifTagValue.MDFileUnits: + case ExifTagValue.PixelScale: + case ExifTagValue.IntergraphPacketData: + case ExifTagValue.IntergraphRegisters: + case ExifTagValue.IntergraphMatrix: + case ExifTagValue.ModelTiePoint: + case ExifTagValue.SEMInfo: + case ExifTagValue.ModelTransform: + case ExifTagValue.ImageLayer: + case ExifTagValue.FaxRecvParams: + case ExifTagValue.FaxSubaddress: + case ExifTagValue.FaxRecvTime: + case ExifTagValue.ImageSourceData: + case ExifTagValue.XPTitle: + case ExifTagValue.XPComment: + case ExifTagValue.XPAuthor: + case ExifTagValue.XPKeywords: + case ExifTagValue.XPSubject: + case ExifTagValue.GDALMetadata: + case ExifTagValue.GDALNoData: + return ExifParts.IfdTags; + + case ExifTagValue.ExposureTime: + case ExifTagValue.FNumber: + case ExifTagValue.ExposureProgram: + case ExifTagValue.SpectralSensitivity: + case ExifTagValue.ISOSpeedRatings: + case ExifTagValue.OECF: + case ExifTagValue.Interlace: + case ExifTagValue.TimeZoneOffset: + case ExifTagValue.SelfTimerMode: + case ExifTagValue.SensitivityType: + case ExifTagValue.StandardOutputSensitivity: + case ExifTagValue.RecommendedExposureIndex: + case ExifTagValue.ISOSpeed: + case ExifTagValue.ISOSpeedLatitudeyyy: + case ExifTagValue.ISOSpeedLatitudezzz: + case ExifTagValue.ExifVersion: + case ExifTagValue.DateTimeOriginal: + case ExifTagValue.DateTimeDigitized: + case ExifTagValue.OffsetTime: + case ExifTagValue.OffsetTimeOriginal: + case ExifTagValue.OffsetTimeDigitized: + case ExifTagValue.ComponentsConfiguration: + case ExifTagValue.CompressedBitsPerPixel: + case ExifTagValue.ShutterSpeedValue: + case ExifTagValue.ApertureValue: + case ExifTagValue.BrightnessValue: + case ExifTagValue.ExposureBiasValue: + case ExifTagValue.MaxApertureValue: + case ExifTagValue.SubjectDistance: + case ExifTagValue.MeteringMode: + case ExifTagValue.LightSource: + case ExifTagValue.Flash: + case ExifTagValue.FocalLength: + case ExifTagValue.FlashEnergy2: + case ExifTagValue.SpatialFrequencyResponse2: + case ExifTagValue.Noise: + case ExifTagValue.FocalPlaneXResolution2: + case ExifTagValue.FocalPlaneYResolution2: + case ExifTagValue.FocalPlaneResolutionUnit2: + case ExifTagValue.ImageNumber: + case ExifTagValue.SecurityClassification: + case ExifTagValue.ImageHistory: + case ExifTagValue.SubjectArea: + case ExifTagValue.ExposureIndex2: + case ExifTagValue.TIFFEPStandardID: + case ExifTagValue.SensingMethod2: + case ExifTagValue.MakerNote: + case ExifTagValue.UserComment: + case ExifTagValue.SubsecTime: + case ExifTagValue.SubsecTimeOriginal: + case ExifTagValue.SubsecTimeDigitized: + case ExifTagValue.AmbientTemperature: + case ExifTagValue.Humidity: + case ExifTagValue.Pressure: + case ExifTagValue.WaterDepth: + case ExifTagValue.Acceleration: + case ExifTagValue.CameraElevationAngle: + case ExifTagValue.FlashpixVersion: + case ExifTagValue.ColorSpace: + case ExifTagValue.PixelXDimension: + case ExifTagValue.PixelYDimension: + case ExifTagValue.RelatedSoundFile: + case ExifTagValue.FlashEnergy: + case ExifTagValue.SpatialFrequencyResponse: + case ExifTagValue.FocalPlaneXResolution: + case ExifTagValue.FocalPlaneYResolution: + case ExifTagValue.FocalPlaneResolutionUnit: + case ExifTagValue.SubjectLocation: + case ExifTagValue.ExposureIndex: + case ExifTagValue.SensingMethod: + case ExifTagValue.FileSource: + case ExifTagValue.SceneType: + case ExifTagValue.CFAPattern: + case ExifTagValue.CustomRendered: + case ExifTagValue.ExposureMode: + case ExifTagValue.WhiteBalance: + case ExifTagValue.DigitalZoomRatio: + case ExifTagValue.FocalLengthIn35mmFilm: + case ExifTagValue.SceneCaptureType: + case ExifTagValue.GainControl: + case ExifTagValue.Contrast: + case ExifTagValue.Saturation: + case ExifTagValue.Sharpness: + case ExifTagValue.DeviceSettingDescription: + case ExifTagValue.SubjectDistanceRange: + case ExifTagValue.ImageUniqueID: + case ExifTagValue.OwnerName: + case ExifTagValue.SerialNumber: + case ExifTagValue.LensSpecification: + case ExifTagValue.LensMake: + case ExifTagValue.LensModel: + case ExifTagValue.LensSerialNumber: + return ExifParts.ExifTags; + + case ExifTagValue.GPSVersionID: + case ExifTagValue.GPSLatitudeRef: + case ExifTagValue.GPSLatitude: + case ExifTagValue.GPSLongitudeRef: + case ExifTagValue.GPSLongitude: + case ExifTagValue.GPSAltitudeRef: + case ExifTagValue.GPSAltitude: + case ExifTagValue.GPSTimestamp: + case ExifTagValue.GPSSatellites: + case ExifTagValue.GPSStatus: + case ExifTagValue.GPSMeasureMode: + case ExifTagValue.GPSDOP: + case ExifTagValue.GPSSpeedRef: + case ExifTagValue.GPSSpeed: + case ExifTagValue.GPSTrackRef: + case ExifTagValue.GPSTrack: + case ExifTagValue.GPSImgDirectionRef: + case ExifTagValue.GPSImgDirection: + case ExifTagValue.GPSMapDatum: + case ExifTagValue.GPSDestLatitudeRef: + case ExifTagValue.GPSDestLatitude: + case ExifTagValue.GPSDestLongitudeRef: + case ExifTagValue.GPSDestLongitude: + case ExifTagValue.GPSDestBearingRef: + case ExifTagValue.GPSDestBearing: + case ExifTagValue.GPSDestDistanceRef: + case ExifTagValue.GPSDestDistance: + case ExifTagValue.GPSProcessingMethod: + case ExifTagValue.GPSAreaInformation: + case ExifTagValue.GPSDateStamp: + case ExifTagValue.GPSDifferential: + return ExifParts.GpsTags; + + case ExifTagValue.Unknown: + case ExifTagValue.SubIFDOffset: + case ExifTagValue.GPSIFDOffset: + default: + return ExifParts.None; + } + } + } +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs similarity index 58% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index 67c1b2b65e..c068461b2f 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -5,7 +5,6 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Text; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { @@ -18,24 +17,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// Which parts will be written. /// private readonly ExifParts allowedParts; - private readonly IList values; + private readonly IList values; private List dataOffsets; - private readonly List ifdIndexes; - private readonly List exifIndexes; - private readonly List gpsIndexes; + private readonly List ifdValues; + private readonly List exifValues; + private readonly List gpsValues; /// /// Initializes a new instance of the class. /// /// The values. /// The allowed parts. - public ExifWriter(IList values, ExifParts allowedParts) + public ExifWriter(IList values, ExifParts allowedParts) { this.values = values; this.allowedParts = allowedParts; - this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd); - this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif); - this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps); + this.ifdValues = this.GetPartValues(ExifParts.IfdTags); + this.exifValues = this.GetPartValues(ExifParts.ExifTags); + this.gpsValues = this.GetPartValues(ExifParts.GpsTags); } /// @@ -46,43 +45,29 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public byte[] GetData() { - uint startIndex = 0; + const uint startIndex = 0; uint length; - int exifIndex = -1; - int gpsIndex = -1; - if (this.exifIndexes.Count > 0) - { - exifIndex = this.GetIndex(this.ifdIndexes, ExifTag.SubIFDOffset); - } - - if (this.gpsIndexes.Count > 0) - { - gpsIndex = this.GetIndex(this.ifdIndexes, ExifTag.GPSIFDOffset); - } - - uint ifdLength = 2 + this.GetLength(this.ifdIndexes) + 4; - uint exifLength = this.GetLength(this.exifIndexes); - uint gpsLength = this.GetLength(this.gpsIndexes); + IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset); + IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset); - if (exifLength > 0) + if (this.ifdValues.Count == 0 && this.exifValues.Count == 0 && this.gpsValues.Count == 0) { - exifLength += 2; + return Array.Empty(); } - if (gpsLength > 0) - { - gpsLength += 2; - } + uint ifdLength = this.GetLength(this.ifdValues) + 4U; + uint exifLength = this.GetLength(this.exifValues); + uint gpsLength = this.GetLength(this.gpsValues); length = ifdLength + exifLength + gpsLength; - if (length == 6) + if (length == 4U) { - return null; + return Array.Empty(); } - // two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total + // two bytes for the byte Order marker 'II' or 'MM', followed by the number 42 (0x2A) and a 0, making 4 bytes total length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length; length += 4 + 2; @@ -91,38 +76,31 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif int i = 0; - // the byte order marker for little-endian, followed by the number 42 and a 0 + // The byte order marker for little-endian, followed by the number 42 and a 0 ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i)); i += ExifConstants.LittleEndianByteOrderMarker.Length; - uint ifdOffset = ((uint)i - startIndex) + 4; + uint ifdOffset = ((uint)i - startIndex) + 4U; uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength; - if (exifLength > 0) - { - this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength); - } - - if (gpsLength > 0) - { - this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength); - } + exifOffset?.TrySetValue(ifdOffset + ifdLength); + gpsOffset?.TrySetValue(ifdOffset + ifdLength + exifLength); i = WriteUInt32(ifdOffset, result, i); - i = this.WriteHeaders(this.ifdIndexes, result, i); + i = this.WriteHeaders(this.ifdValues, result, i); i = WriteUInt32(thumbnailOffset, result, i); - i = this.WriteData(startIndex, this.ifdIndexes, result, i); + i = this.WriteData(startIndex, this.ifdValues, result, i); if (exifLength > 0) { - i = this.WriteHeaders(this.exifIndexes, result, i); - i = this.WriteData(startIndex, this.exifIndexes, result, i); + i = this.WriteHeaders(this.exifValues, result, i); + i = this.WriteData(startIndex, this.exifValues, result, i); } if (gpsLength > 0) { - i = this.WriteHeaders(this.gpsIndexes, result, i); - i = this.WriteData(startIndex, this.gpsIndexes, result, i); + i = this.WriteHeaders(this.gpsValues, result, i); + i = this.WriteData(startIndex, this.gpsValues, result, i); } WriteUInt16(0, result, i); @@ -179,79 +157,137 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return offset + 4; } - private int GetIndex(IList indexes, ExifTag tag) + private static IExifValue GetOffsetValue(List ifdValues, List values, ExifTag offset) { - foreach (int index in indexes) + int index = -1; + + for (int i = 0; i < ifdValues.Count; i++) { - if (this.values[index].Tag == tag) + if (ifdValues[i].Tag == offset) { - return index; + index = i; } } - int newIndex = this.values.Count; - indexes.Add(newIndex); - this.values.Add(ExifValue.Create(tag, null)); - return newIndex; + if (values.Count > 0) + { + if (index != -1) + { + return ifdValues[index]; + } + + ExifValue result = ExifValues.Create(offset); + ifdValues.Add(result); + + return result; + } + else if (index != -1) + { + ifdValues.RemoveAt(index); + } + + return null; } - private List GetIndexes(ExifParts part, ExifTag[] tags) + private List GetPartValues(ExifParts part) { - if (((int)this.allowedParts & (int)part) == 0) + var result = new List(); + + if (!EnumUtils.HasFlag(this.allowedParts, part)) { - return new List(); + return result; } - var result = new List(); - for (int i = 0; i < this.values.Count; i++) + foreach (IExifValue value in this.values) { - ExifValue value = this.values[i]; - - if (!value.HasValue) + if (!HasValue(value)) { continue; } - int index = Array.IndexOf(tags, value.Tag); - if (index > -1) + if (ExifTags.GetPart(value.Tag) == part) { - result.Add(i); + result.Add(value); } } return result; } - private uint GetLength(IList indexes) + private static bool HasValue(IExifValue exifValue) + { + object value = exifValue.GetValue(); + if (value is null) + { + return false; + } + + if (exifValue.DataType == ExifDataType.Ascii) + { + string stringValue = (string)value; + return stringValue.Length > 0; + } + + if (value is Array arrayValue) + { + return arrayValue.Length > 0; + } + + return true; + } + + private uint GetLength(IList values) { - uint length = 0; + if (values.Count == 0) + { + return 0; + } - foreach (int index in indexes) + uint length = 2; + + foreach (IExifValue value in values) { - uint valueLength = (uint)this.values[index].Length; + uint valueLength = GetLength(value); + + length += 2 + 2 + 4 + 4; if (valueLength > 4) { - length += 12 + valueLength; - } - else - { - length += 12; + length += valueLength; } } return length; } - private int WriteArray(ExifValue value, Span destination, int offset) + private static uint GetLength(IExifValue value) => GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType); + + private static uint GetNumberOfComponents(IExifValue exifValue) + { + object value = exifValue.GetValue(); + + if (exifValue.DataType == ExifDataType.Ascii) + { + return (uint)Encoding.UTF8.GetBytes((string)value).Length + 1; + } + + if (value is Array arrayValue) + { + return (uint)arrayValue.Length; + } + + return 1; + } + + private int WriteArray(IExifValue value, Span destination, int offset) { if (value.DataType == ExifDataType.Ascii) { - return this.WriteValue(ExifDataType.Ascii, value.Value, destination, offset); + return this.WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset); } int newOffset = offset; - foreach (object obj in (Array)value.Value) + foreach (object obj in (Array)value.GetValue()) { newOffset = this.WriteValue(value.DataType, obj, destination, newOffset); } @@ -259,7 +295,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return newOffset; } - private int WriteData(uint startIndex, List indexes, Span destination, int offset) + private int WriteData(uint startIndex, List values, Span destination, int offset) { if (this.dataOffsets.Count == 0) { @@ -269,10 +305,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif int newOffset = offset; int i = 0; - foreach (int index in indexes) + foreach (IExifValue value in values) { - ExifValue value = this.values[index]; - if (value.Length > 4) + if (GetLength(value) > 4) { WriteUInt32((uint)(newOffset - startIndex), destination, this.dataOffsets[i++]); newOffset = this.WriteValue(value, destination, newOffset); @@ -282,25 +317,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return newOffset; } - private int WriteHeaders(List indexes, Span destination, int offset) + private int WriteHeaders(List values, Span destination, int offset) { this.dataOffsets = new List(); - int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset); + int newOffset = WriteUInt16((ushort)values.Count, destination, offset); - if (indexes.Count == 0) + if (values.Count == 0) { return newOffset; } - foreach (int index in indexes) + foreach (IExifValue value in values) { - ExifValue value = this.values[index]; newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset); newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset); - newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset); + newOffset = WriteUInt32(GetNumberOfComponents(value), destination, newOffset); - if (value.Length > 4) + uint length = GetLength(value); + if (length > 4) { this.dataOffsets.Add(newOffset); } @@ -332,7 +367,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (dataType) { case ExifDataType.Ascii: - return Write(Encoding.UTF8.GetBytes((string)value), destination, offset); + offset = Write(Encoding.UTF8.GetBytes((string)value), destination, offset); + destination[offset] = 0; + return offset + 1; case ExifDataType.Byte: case ExifDataType.Undefined: destination[offset] = (byte)value; @@ -340,8 +377,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifDataType.DoubleFloat: return WriteDouble((double)value, destination, offset); case ExifDataType.Short: + if (value is Number shortNumber) + { + return WriteUInt16((ushort)shortNumber, destination, offset); + } + return WriteUInt16((ushort)value, destination, offset); case ExifDataType.Long: + if (value is Number longNumber) + { + return WriteUInt32((uint)longNumber, destination, offset); + } + return WriteUInt32((uint)value, destination, offset); case ExifDataType.Rational: WriteRational(destination.Slice(offset, 8), (Rational)value); @@ -363,14 +410,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - private int WriteValue(ExifValue value, Span destination, int offset) + private int WriteValue(IExifValue value, Span destination, int offset) { if (value.IsArray && value.DataType != ExifDataType.Ascii) { return this.WriteArray(value, destination, offset); } - return this.WriteValue(value.DataType, value.Value, destination, offset); + return this.WriteValue(value.DataType, value.GetValue(), destination, offset); } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/README.md b/src/ImageSharp/Metadata/Profiles/Exif/README.md new file mode 100644 index 0000000000..7901527e1a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/README.md @@ -0,0 +1,3 @@ +Adapted from Magick.NET: + +https://github.com/dlemstra/Magick.NET diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs new file mode 100644 index 0000000000..dc33fd8b0a --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the FaxProfile exif tag. + /// + public static ExifTag FaxProfile { get; } = new ExifTag(ExifTagValue.FaxProfile); + + /// + /// Gets the ModeNumber exif tag. + /// + public static ExifTag ModeNumber { get; } = new ExifTag(ExifTagValue.ModeNumber); + + /// + /// Gets the GPSAltitudeRef exif tag. + /// + public static ExifTag GPSAltitudeRef { get; } = new ExifTag(ExifTagValue.GPSAltitudeRef); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs new file mode 100644 index 0000000000..2bfa8ff213 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ClipPath exif tag. + /// + public static ExifTag ClipPath => new ExifTag(ExifTagValue.ClipPath); + + /// + /// Gets the VersionYear exif tag. + /// + public static ExifTag VersionYear => new ExifTag(ExifTagValue.VersionYear); + + /// + /// Gets the XMP exif tag. + /// + public static ExifTag XMP => new ExifTag(ExifTagValue.XMP); + + /// + /// Gets the CFAPattern2 exif tag. + /// + public static ExifTag CFAPattern2 => new ExifTag(ExifTagValue.CFAPattern2); + + /// + /// Gets the TIFFEPStandardID exif tag. + /// + public static ExifTag TIFFEPStandardID => new ExifTag(ExifTagValue.TIFFEPStandardID); + + /// + /// Gets the XPTitle exif tag. + /// + public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle); + + /// + /// Gets the XPComment exif tag. + /// + public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment); + + /// + /// Gets the XPAuthor exif tag. + /// + public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor); + + /// + /// Gets the XPKeywords exif tag. + /// + public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords); + + /// + /// Gets the XPSubject exif tag. + /// + public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject); + + /// + /// Gets the GPSVersionID exif tag. + /// + public static ExifTag GPSVersionID => new ExifTag(ExifTagValue.GPSVersionID); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs new file mode 100644 index 0000000000..6cbae4c558 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the PixelScale exif tag. + /// + public static ExifTag PixelScale { get; } = new ExifTag(ExifTagValue.PixelScale); + + /// + /// Gets the IntergraphMatrix exif tag. + /// + public static ExifTag IntergraphMatrix { get; } = new ExifTag(ExifTagValue.IntergraphMatrix); + + /// + /// Gets the ModelTiePoint exif tag. + /// + public static ExifTag ModelTiePoint { get; } = new ExifTag(ExifTagValue.ModelTiePoint); + + /// + /// Gets the ModelTransform exif tag. + /// + public static ExifTag ModelTransform { get; } = new ExifTag(ExifTagValue.ModelTransform); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs new file mode 100644 index 0000000000..571b50efb6 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the SubfileType exif tag. + /// + public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType); + + /// + /// Gets the SubIFDOffset exif tag. + /// + public static ExifTag SubIFDOffset { get; } = new ExifTag(ExifTagValue.SubIFDOffset); + + /// + /// Gets the GPSIFDOffset exif tag. + /// + public static ExifTag GPSIFDOffset { get; } = new ExifTag(ExifTagValue.GPSIFDOffset); + + /// + /// Gets the T4Options exif tag. + /// + public static ExifTag T4Options { get; } = new ExifTag(ExifTagValue.T4Options); + + /// + /// Gets the T6Options exif tag. + /// + public static ExifTag T6Options { get; } = new ExifTag(ExifTagValue.T6Options); + + /// + /// Gets the XClipPathUnits exif tag. + /// + public static ExifTag XClipPathUnits { get; } = new ExifTag(ExifTagValue.XClipPathUnits); + + /// + /// Gets the YClipPathUnits exif tag. + /// + public static ExifTag YClipPathUnits { get; } = new ExifTag(ExifTagValue.YClipPathUnits); + + /// + /// Gets the ProfileType exif tag. + /// + public static ExifTag ProfileType { get; } = new ExifTag(ExifTagValue.ProfileType); + + /// + /// Gets the CodingMethods exif tag. + /// + public static ExifTag CodingMethods { get; } = new ExifTag(ExifTagValue.CodingMethods); + + /// + /// Gets the T82ptions exif tag. + /// + public static ExifTag T82ptions { get; } = new ExifTag(ExifTagValue.T82ptions); + + /// + /// Gets the JPEGInterchangeFormat exif tag. + /// + public static ExifTag JPEGInterchangeFormat { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormat); + + /// + /// Gets the JPEGInterchangeFormatLength exif tag. + /// + public static ExifTag JPEGInterchangeFormatLength { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormatLength); + + /// + /// Gets the MDFileTag exif tag. + /// + public static ExifTag MDFileTag { get; } = new ExifTag(ExifTagValue.MDFileTag); + + /// + /// Gets the StandardOutputSensitivity exif tag. + /// + public static ExifTag StandardOutputSensitivity { get; } = new ExifTag(ExifTagValue.StandardOutputSensitivity); + + /// + /// Gets the RecommendedExposureIndex exif tag. + /// + public static ExifTag RecommendedExposureIndex { get; } = new ExifTag(ExifTagValue.RecommendedExposureIndex); + + /// + /// Gets the ISOSpeed exif tag. + /// + public static ExifTag ISOSpeed { get; } = new ExifTag(ExifTagValue.ISOSpeed); + + /// + /// Gets the ISOSpeedLatitudeyyy exif tag. + /// + public static ExifTag ISOSpeedLatitudeyyy { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudeyyy); + + /// + /// Gets the ISOSpeedLatitudezzz exif tag. + /// + public static ExifTag ISOSpeedLatitudezzz { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudezzz); + + /// + /// Gets the FaxRecvParams exif tag. + /// + public static ExifTag FaxRecvParams { get; } = new ExifTag(ExifTagValue.FaxRecvParams); + + /// + /// Gets the FaxRecvTime exif tag. + /// + public static ExifTag FaxRecvTime { get; } = new ExifTag(ExifTagValue.FaxRecvTime); + + /// + /// Gets the ImageNumber exif tag. + /// + public static ExifTag ImageNumber { get; } = new ExifTag(ExifTagValue.ImageNumber); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs new file mode 100644 index 0000000000..120f2dab0f --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the FreeOffsets exif tag. + /// + public static ExifTag FreeOffsets { get; } = new ExifTag(ExifTagValue.FreeOffsets); + + /// + /// Gets the FreeByteCounts exif tag. + /// + public static ExifTag FreeByteCounts { get; } = new ExifTag(ExifTagValue.FreeByteCounts); + + /// + /// Gets the ColorResponseUnit exif tag. + /// + public static ExifTag ColorResponseUnit { get; } = new ExifTag(ExifTagValue.ColorResponseUnit); + + /// + /// Gets the TileOffsets exif tag. + /// + public static ExifTag TileOffsets { get; } = new ExifTag(ExifTagValue.TileOffsets); + + /// + /// Gets the SMinSampleValue exif tag. + /// + public static ExifTag SMinSampleValue { get; } = new ExifTag(ExifTagValue.SMinSampleValue); + + /// + /// Gets the SMaxSampleValue exif tag. + /// + public static ExifTag SMaxSampleValue { get; } = new ExifTag(ExifTagValue.SMaxSampleValue); + + /// + /// Gets the JPEGQTables exif tag. + /// + public static ExifTag JPEGQTables { get; } = new ExifTag(ExifTagValue.JPEGQTables); + + /// + /// Gets the JPEGDCTables exif tag. + /// + public static ExifTag JPEGDCTables { get; } = new ExifTag(ExifTagValue.JPEGDCTables); + + /// + /// Gets the JPEGACTables exif tag. + /// + public static ExifTag JPEGACTables { get; } = new ExifTag(ExifTagValue.JPEGACTables); + + /// + /// Gets the StripRowCounts exif tag. + /// + public static ExifTag StripRowCounts { get; } = new ExifTag(ExifTagValue.StripRowCounts); + + /// + /// Gets the IntergraphRegisters exif tag. + /// + public static ExifTag IntergraphRegisters { get; } = new ExifTag(ExifTagValue.IntergraphRegisters); + + /// + /// Gets the TimeZoneOffset exif tag. + /// + public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs new file mode 100644 index 0000000000..6cea52b1a5 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Number.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ImageWidth exif tag. + /// + public static ExifTag ImageWidth { get; } = new ExifTag(ExifTagValue.ImageWidth); + + /// + /// Gets the ImageLength exif tag. + /// + public static ExifTag ImageLength { get; } = new ExifTag(ExifTagValue.ImageLength); + + /// + /// Gets the TileWidth exif tag. + /// + public static ExifTag TileWidth { get; } = new ExifTag(ExifTagValue.TileWidth); + + /// + /// Gets the TileLength exif tag. + /// + public static ExifTag TileLength { get; } = new ExifTag(ExifTagValue.TileLength); + + /// + /// Gets the BadFaxLines exif tag. + /// + public static ExifTag BadFaxLines { get; } = new ExifTag(ExifTagValue.BadFaxLines); + + /// + /// Gets the ConsecutiveBadFaxLines exif tag. + /// + public static ExifTag ConsecutiveBadFaxLines { get; } = new ExifTag(ExifTagValue.ConsecutiveBadFaxLines); + + /// + /// Gets the PixelXDimension exif tag. + /// + public static ExifTag PixelXDimension { get; } = new ExifTag(ExifTagValue.PixelXDimension); + + /// + /// Gets the PixelYDimension exif tag. + /// + public static ExifTag PixelYDimension { get; } = new ExifTag(ExifTagValue.PixelYDimension); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs new file mode 100644 index 0000000000..b515ab36a5 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.NumberArray.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the StripOffsets exif tag. + /// + public static ExifTag StripOffsets { get; } = new ExifTag(ExifTagValue.StripOffsets); + + /// + /// Gets the TileByteCounts exif tag. + /// + public static ExifTag TileByteCounts { get; } = new ExifTag(ExifTagValue.TileByteCounts); + + /// + /// Gets the ImageLayer exif tag. + /// + public static ExifTag ImageLayer { get; } = new ExifTag(ExifTagValue.ImageLayer); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs new file mode 100644 index 0000000000..2281dee49c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Rational.cs @@ -0,0 +1,169 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the XPosition exif tag. + /// + public static ExifTag XPosition { get; } = new ExifTag(ExifTagValue.XPosition); + + /// + /// Gets the YPosition exif tag. + /// + public static ExifTag YPosition { get; } = new ExifTag(ExifTagValue.YPosition); + + /// + /// Gets the XResolution exif tag. + /// + public static ExifTag XResolution { get; } = new ExifTag(ExifTagValue.XResolution); + + /// + /// Gets the YResolution exif tag. + /// + public static ExifTag YResolution { get; } = new ExifTag(ExifTagValue.YResolution); + + /// + /// Gets the BatteryLevel exif tag. + /// + public static ExifTag BatteryLevel { get; } = new ExifTag(ExifTagValue.BatteryLevel); + + /// + /// Gets the ExposureTime exif tag. + /// + public static ExifTag ExposureTime { get; } = new ExifTag(ExifTagValue.ExposureTime); + + /// + /// Gets the FNumber exif tag. + /// + public static ExifTag FNumber { get; } = new ExifTag(ExifTagValue.FNumber); + + /// + /// Gets the MDScalePixel exif tag. + /// + public static ExifTag MDScalePixel { get; } = new ExifTag(ExifTagValue.MDScalePixel); + + /// + /// Gets the CompressedBitsPerPixel exif tag. + /// + public static ExifTag CompressedBitsPerPixel { get; } = new ExifTag(ExifTagValue.CompressedBitsPerPixel); + + /// + /// Gets the ApertureValue exif tag. + /// + public static ExifTag ApertureValue { get; } = new ExifTag(ExifTagValue.ApertureValue); + + /// + /// Gets the MaxApertureValue exif tag. + /// + public static ExifTag MaxApertureValue { get; } = new ExifTag(ExifTagValue.MaxApertureValue); + + /// + /// Gets the SubjectDistance exif tag. + /// + public static ExifTag SubjectDistance { get; } = new ExifTag(ExifTagValue.SubjectDistance); + + /// + /// Gets the FocalLength exif tag. + /// + public static ExifTag FocalLength { get; } = new ExifTag(ExifTagValue.FocalLength); + + /// + /// Gets the FlashEnergy2 exif tag. + /// + public static ExifTag FlashEnergy2 { get; } = new ExifTag(ExifTagValue.FlashEnergy2); + + /// + /// Gets the FocalPlaneXResolution2 exif tag. + /// + public static ExifTag FocalPlaneXResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution2); + + /// + /// Gets the FocalPlaneYResolution2 exif tag. + /// + public static ExifTag FocalPlaneYResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution2); + + /// + /// Gets the ExposureIndex2 exif tag. + /// + public static ExifTag ExposureIndex2 { get; } = new ExifTag(ExifTagValue.ExposureIndex2); + + /// + /// Gets the Humidity exif tag. + /// + public static ExifTag Humidity { get; } = new ExifTag(ExifTagValue.Humidity); + + /// + /// Gets the Pressure exif tag. + /// + public static ExifTag Pressure { get; } = new ExifTag(ExifTagValue.Pressure); + + /// + /// Gets the Acceleration exif tag. + /// + public static ExifTag Acceleration { get; } = new ExifTag(ExifTagValue.Acceleration); + + /// + /// Gets the FlashEnergy exif tag. + /// + public static ExifTag FlashEnergy { get; } = new ExifTag(ExifTagValue.FlashEnergy); + + /// + /// Gets the FocalPlaneXResolution exif tag. + /// + public static ExifTag FocalPlaneXResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution); + + /// + /// Gets the FocalPlaneYResolution exif tag. + /// + public static ExifTag FocalPlaneYResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution); + + /// + /// Gets the ExposureIndex exif tag. + /// + public static ExifTag ExposureIndex { get; } = new ExifTag(ExifTagValue.ExposureIndex); + + /// + /// Gets the DigitalZoomRatio exif tag. + /// + public static ExifTag DigitalZoomRatio { get; } = new ExifTag(ExifTagValue.DigitalZoomRatio); + + /// + /// Gets the GPSAltitude exif tag. + /// + public static ExifTag GPSAltitude { get; } = new ExifTag(ExifTagValue.GPSAltitude); + + /// + /// Gets the GPSDOP exif tag. + /// + public static ExifTag GPSDOP { get; } = new ExifTag(ExifTagValue.GPSDOP); + + /// + /// Gets the GPSSpeed exif tag. + /// + public static ExifTag GPSSpeed { get; } = new ExifTag(ExifTagValue.GPSSpeed); + + /// + /// Gets the GPSTrack exif tag. + /// + public static ExifTag GPSTrack { get; } = new ExifTag(ExifTagValue.GPSTrack); + + /// + /// Gets the GPSImgDirection exif tag. + /// + public static ExifTag GPSImgDirection { get; } = new ExifTag(ExifTagValue.GPSImgDirection); + + /// + /// Gets the GPSDestBearing exif tag. + /// + public static ExifTag GPSDestBearing { get; } = new ExifTag(ExifTagValue.GPSDestBearing); + + /// + /// Gets the GPSDestDistance exif tag. + /// + public static ExifTag GPSDestDistance { get; } = new ExifTag(ExifTagValue.GPSDestDistance); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs new file mode 100644 index 0000000000..cf43a8a8a4 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.RationalArray.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the WhitePoint exif tag. + /// + public static ExifTag WhitePoint { get; } = new ExifTag(ExifTagValue.WhitePoint); + + /// + /// Gets the PrimaryChromaticities exif tag. + /// + public static ExifTag PrimaryChromaticities { get; } = new ExifTag(ExifTagValue.PrimaryChromaticities); + + /// + /// Gets the YCbCrCoefficients exif tag. + /// + public static ExifTag YCbCrCoefficients { get; } = new ExifTag(ExifTagValue.YCbCrCoefficients); + + /// + /// Gets the ReferenceBlackWhite exif tag. + /// + public static ExifTag ReferenceBlackWhite { get; } = new ExifTag(ExifTagValue.ReferenceBlackWhite); + + /// + /// Gets the GPSLatitude exif tag. + /// + public static ExifTag GPSLatitude { get; } = new ExifTag(ExifTagValue.GPSLatitude); + + /// + /// Gets the GPSLongitude exif tag. + /// + public static ExifTag GPSLongitude { get; } = new ExifTag(ExifTagValue.GPSLongitude); + + /// + /// Gets the GPSTimestamp exif tag. + /// + public static ExifTag GPSTimestamp { get; } = new ExifTag(ExifTagValue.GPSTimestamp); + + /// + /// Gets the GPSDestLatitude exif tag. + /// + public static ExifTag GPSDestLatitude { get; } = new ExifTag(ExifTagValue.GPSDestLatitude); + + /// + /// Gets the GPSDestLongitude exif tag. + /// + public static ExifTag GPSDestLongitude { get; } = new ExifTag(ExifTagValue.GPSDestLongitude); + + /// + /// Gets the LensSpecification exif tag. + /// + public static ExifTag LensSpecification { get; } = new ExifTag(ExifTagValue.LensSpecification); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs new file mode 100644 index 0000000000..7fe9a58bcd --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Short.cs @@ -0,0 +1,239 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the OldSubfileType exif tag. + /// + public static ExifTag OldSubfileType { get; } = new ExifTag(ExifTagValue.OldSubfileType); + + /// + /// Gets the Compression exif tag. + /// + public static ExifTag Compression { get; } = new ExifTag(ExifTagValue.Compression); + + /// + /// Gets the PhotometricInterpretation exif tag. + /// + public static ExifTag PhotometricInterpretation { get; } = new ExifTag(ExifTagValue.PhotometricInterpretation); + + /// + /// Gets the Thresholding exif tag. + /// + public static ExifTag Thresholding { get; } = new ExifTag(ExifTagValue.Thresholding); + + /// + /// Gets the CellWidth exif tag. + /// + public static ExifTag CellWidth { get; } = new ExifTag(ExifTagValue.CellWidth); + + /// + /// Gets the CellLength exif tag. + /// + public static ExifTag CellLength { get; } = new ExifTag(ExifTagValue.CellLength); + + /// + /// Gets the FillOrder exif tag. + /// + public static ExifTag FillOrder { get; } = new ExifTag(ExifTagValue.FillOrder); + + /// + /// Gets the Orientation exif tag. + /// + public static ExifTag Orientation { get; } = new ExifTag(ExifTagValue.Orientation); + + /// + /// Gets the SamplesPerPixel exif tag. + /// + public static ExifTag SamplesPerPixel { get; } = new ExifTag(ExifTagValue.SamplesPerPixel); + + /// + /// Gets the PlanarConfiguration exif tag. + /// + public static ExifTag PlanarConfiguration { get; } = new ExifTag(ExifTagValue.PlanarConfiguration); + + /// + /// Gets the GrayResponseUnit exif tag. + /// + public static ExifTag GrayResponseUnit { get; } = new ExifTag(ExifTagValue.GrayResponseUnit); + + /// + /// Gets the ResolutionUnit exif tag. + /// + public static ExifTag ResolutionUnit { get; } = new ExifTag(ExifTagValue.ResolutionUnit); + + /// + /// Gets the CleanFaxData exif tag. + /// + public static ExifTag CleanFaxData { get; } = new ExifTag(ExifTagValue.CleanFaxData); + + /// + /// Gets the InkSet exif tag. + /// + public static ExifTag InkSet { get; } = new ExifTag(ExifTagValue.InkSet); + + /// + /// Gets the NumberOfInks exif tag. + /// + public static ExifTag NumberOfInks { get; } = new ExifTag(ExifTagValue.NumberOfInks); + + /// + /// Gets the DotRange exif tag. + /// + public static ExifTag DotRange { get; } = new ExifTag(ExifTagValue.DotRange); + + /// + /// Gets the Indexed exif tag. + /// + public static ExifTag Indexed { get; } = new ExifTag(ExifTagValue.Indexed); + + /// + /// Gets the OPIProxy exif tag. + /// + public static ExifTag OPIProxy { get; } = new ExifTag(ExifTagValue.OPIProxy); + + /// + /// Gets the JPEGProc exif tag. + /// + public static ExifTag JPEGProc { get; } = new ExifTag(ExifTagValue.JPEGProc); + + /// + /// Gets the JPEGRestartInterval exif tag. + /// + public static ExifTag JPEGRestartInterval { get; } = new ExifTag(ExifTagValue.JPEGRestartInterval); + + /// + /// Gets the YCbCrPositioning exif tag. + /// + public static ExifTag YCbCrPositioning { get; } = new ExifTag(ExifTagValue.YCbCrPositioning); + + /// + /// Gets the Rating exif tag. + /// + public static ExifTag Rating { get; } = new ExifTag(ExifTagValue.Rating); + + /// + /// Gets the RatingPercent exif tag. + /// + public static ExifTag RatingPercent { get; } = new ExifTag(ExifTagValue.RatingPercent); + + /// + /// Gets the ExposureProgram exif tag. + /// + public static ExifTag ExposureProgram { get; } = new ExifTag(ExifTagValue.ExposureProgram); + + /// + /// Gets the Interlace exif tag. + /// + public static ExifTag Interlace { get; } = new ExifTag(ExifTagValue.Interlace); + + /// + /// Gets the SelfTimerMode exif tag. + /// + public static ExifTag SelfTimerMode { get; } = new ExifTag(ExifTagValue.SelfTimerMode); + + /// + /// Gets the SensitivityType exif tag. + /// + public static ExifTag SensitivityType { get; } = new ExifTag(ExifTagValue.SensitivityType); + + /// + /// Gets the MeteringMode exif tag. + /// + public static ExifTag MeteringMode { get; } = new ExifTag(ExifTagValue.MeteringMode); + + /// + /// Gets the LightSource exif tag. + /// + public static ExifTag LightSource { get; } = new ExifTag(ExifTagValue.LightSource); + + /// + /// Gets the FocalPlaneResolutionUnit2 exif tag. + /// + public static ExifTag FocalPlaneResolutionUnit2 { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit2); + + /// + /// Gets the SensingMethod2 exif tag. + /// + public static ExifTag SensingMethod2 { get; } = new ExifTag(ExifTagValue.SensingMethod2); + + /// + /// Gets the Flash exif tag. + /// + public static ExifTag Flash { get; } = new ExifTag(ExifTagValue.Flash); + + /// + /// Gets the ColorSpace exif tag. + /// + public static ExifTag ColorSpace { get; } = new ExifTag(ExifTagValue.ColorSpace); + + /// + /// Gets the FocalPlaneResolutionUnit exif tag. + /// + public static ExifTag FocalPlaneResolutionUnit { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit); + + /// + /// Gets the SensingMethod exif tag. + /// + public static ExifTag SensingMethod { get; } = new ExifTag(ExifTagValue.SensingMethod); + + /// + /// Gets the CustomRendered exif tag. + /// + public static ExifTag CustomRendered { get; } = new ExifTag(ExifTagValue.CustomRendered); + + /// + /// Gets the ExposureMode exif tag. + /// + public static ExifTag ExposureMode { get; } = new ExifTag(ExifTagValue.ExposureMode); + + /// + /// Gets the WhiteBalance exif tag. + /// + public static ExifTag WhiteBalance { get; } = new ExifTag(ExifTagValue.WhiteBalance); + + /// + /// Gets the FocalLengthIn35mmFilm exif tag. + /// + public static ExifTag FocalLengthIn35mmFilm { get; } = new ExifTag(ExifTagValue.FocalLengthIn35mmFilm); + + /// + /// Gets the SceneCaptureType exif tag. + /// + public static ExifTag SceneCaptureType { get; } = new ExifTag(ExifTagValue.SceneCaptureType); + + /// + /// Gets the GainControl exif tag. + /// + public static ExifTag GainControl { get; } = new ExifTag(ExifTagValue.GainControl); + + /// + /// Gets the Contrast exif tag. + /// + public static ExifTag Contrast { get; } = new ExifTag(ExifTagValue.Contrast); + + /// + /// Gets the Saturation exif tag. + /// + public static ExifTag Saturation { get; } = new ExifTag(ExifTagValue.Saturation); + + /// + /// Gets the Sharpness exif tag. + /// + public static ExifTag Sharpness { get; } = new ExifTag(ExifTagValue.Sharpness); + + /// + /// Gets the SubjectDistanceRange exif tag. + /// + public static ExifTag SubjectDistanceRange { get; } = new ExifTag(ExifTagValue.SubjectDistanceRange); + + /// + /// Gets the GPSDifferential exif tag. + /// + public static ExifTag GPSDifferential { get; } = new ExifTag(ExifTagValue.GPSDifferential); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs new file mode 100644 index 0000000000..90485f75ab --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ShortArray.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the BitsPerSample exif tag. + /// + public static ExifTag BitsPerSample { get; } = new ExifTag(ExifTagValue.BitsPerSample); + + /// + /// Gets the MinSampleValue exif tag. + /// + public static ExifTag MinSampleValue { get; } = new ExifTag(ExifTagValue.MinSampleValue); + + /// + /// Gets the MaxSampleValue exif tag. + /// + public static ExifTag MaxSampleValue { get; } = new ExifTag(ExifTagValue.MaxSampleValue); + + /// + /// Gets the GrayResponseCurve exif tag. + /// + public static ExifTag GrayResponseCurve { get; } = new ExifTag(ExifTagValue.GrayResponseCurve); + + /// + /// Gets the ColorMap exif tag. + /// + public static ExifTag ColorMap { get; } = new ExifTag(ExifTagValue.ColorMap); + + /// + /// Gets the ExtraSamples exif tag. + /// + public static ExifTag ExtraSamples { get; } = new ExifTag(ExifTagValue.ExtraSamples); + + /// + /// Gets the PageNumber exif tag. + /// + public static ExifTag PageNumber { get; } = new ExifTag(ExifTagValue.PageNumber); + + /// + /// Gets the TransferFunction exif tag. + /// + public static ExifTag TransferFunction { get; } = new ExifTag(ExifTagValue.TransferFunction); + + /// + /// Gets the Predictor exif tag. + /// + public static ExifTag Predictor { get; } = new ExifTag(ExifTagValue.Predictor); + + /// + /// Gets the HalftoneHints exif tag. + /// + public static ExifTag HalftoneHints { get; } = new ExifTag(ExifTagValue.HalftoneHints); + + /// + /// Gets the SampleFormat exif tag. + /// + public static ExifTag SampleFormat { get; } = new ExifTag(ExifTagValue.SampleFormat); + + /// + /// Gets the TransferRange exif tag. + /// + public static ExifTag TransferRange { get; } = new ExifTag(ExifTagValue.TransferRange); + + /// + /// Gets the DefaultImageColor exif tag. + /// + public static ExifTag DefaultImageColor { get; } = new ExifTag(ExifTagValue.DefaultImageColor); + + /// + /// Gets the JPEGLosslessPredictors exif tag. + /// + public static ExifTag JPEGLosslessPredictors { get; } = new ExifTag(ExifTagValue.JPEGLosslessPredictors); + + /// + /// Gets the JPEGPointTransforms exif tag. + /// + public static ExifTag JPEGPointTransforms { get; } = new ExifTag(ExifTagValue.JPEGPointTransforms); + + /// + /// Gets the YCbCrSubsampling exif tag. + /// + public static ExifTag YCbCrSubsampling { get; } = new ExifTag(ExifTagValue.YCbCrSubsampling); + + /// + /// Gets the CFARepeatPatternDim exif tag. + /// + public static ExifTag CFARepeatPatternDim { get; } = new ExifTag(ExifTagValue.CFARepeatPatternDim); + + /// + /// Gets the IntergraphPacketData exif tag. + /// + public static ExifTag IntergraphPacketData { get; } = new ExifTag(ExifTagValue.IntergraphPacketData); + + /// + /// Gets the ISOSpeedRatings exif tag. + /// + public static ExifTag ISOSpeedRatings { get; } = new ExifTag(ExifTagValue.ISOSpeedRatings); + + /// + /// Gets the SubjectArea exif tag. + /// + public static ExifTag SubjectArea { get; } = new ExifTag(ExifTagValue.SubjectArea); + + /// + /// Gets the SubjectLocation exif tag. + /// + public static ExifTag SubjectLocation { get; } = new ExifTag(ExifTagValue.SubjectLocation); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs new file mode 100644 index 0000000000..29d61db88c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRational.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ShutterSpeedValue exif tag. + /// + public static ExifTag ShutterSpeedValue { get; } = new ExifTag(ExifTagValue.ShutterSpeedValue); + + /// + /// Gets the BrightnessValue exif tag. + /// + public static ExifTag BrightnessValue { get; } = new ExifTag(ExifTagValue.BrightnessValue); + + /// + /// Gets the ExposureBiasValue exif tag. + /// + public static ExifTag ExposureBiasValue { get; } = new ExifTag(ExifTagValue.ExposureBiasValue); + + /// + /// Gets the AmbientTemperature exif tag. + /// + public static ExifTag AmbientTemperature { get; } = new ExifTag(ExifTagValue.AmbientTemperature); + + /// + /// Gets the WaterDepth exif tag. + /// + public static ExifTag WaterDepth { get; } = new ExifTag(ExifTagValue.WaterDepth); + + /// + /// Gets the CameraElevationAngle exif tag. + /// + public static ExifTag CameraElevationAngle { get; } = new ExifTag(ExifTagValue.CameraElevationAngle); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs new file mode 100644 index 0000000000..9a6e3063b0 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.SignedRationalArray.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the Decode exif tag. + /// + public static ExifTag Decode { get; } = new ExifTag(ExifTagValue.Decode); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs new file mode 100644 index 0000000000..506f874548 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs @@ -0,0 +1,279 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the ImageDescription exif tag. + /// + public static ExifTag ImageDescription { get; } = new ExifTag(ExifTagValue.ImageDescription); + + /// + /// Gets the Make exif tag. + /// + public static ExifTag Make { get; } = new ExifTag(ExifTagValue.Make); + + /// + /// Gets the Model exif tag. + /// + public static ExifTag Model { get; } = new ExifTag(ExifTagValue.Model); + + /// + /// Gets the Software exif tag. + /// + public static ExifTag Software { get; } = new ExifTag(ExifTagValue.Software); + + /// + /// Gets the DateTime exif tag. + /// + public static ExifTag DateTime { get; } = new ExifTag(ExifTagValue.DateTime); + + /// + /// Gets the Artist exif tag. + /// + public static ExifTag Artist { get; } = new ExifTag(ExifTagValue.Artist); + + /// + /// Gets the HostComputer exif tag. + /// + public static ExifTag HostComputer { get; } = new ExifTag(ExifTagValue.HostComputer); + + /// + /// Gets the Copyright exif tag. + /// + public static ExifTag Copyright { get; } = new ExifTag(ExifTagValue.Copyright); + + /// + /// Gets the DocumentName exif tag. + /// + public static ExifTag DocumentName { get; } = new ExifTag(ExifTagValue.DocumentName); + + /// + /// Gets the PageName exif tag. + /// + public static ExifTag PageName { get; } = new ExifTag(ExifTagValue.PageName); + + /// + /// Gets the InkNames exif tag. + /// + public static ExifTag InkNames { get; } = new ExifTag(ExifTagValue.InkNames); + + /// + /// Gets the TargetPrinter exif tag. + /// + public static ExifTag TargetPrinter { get; } = new ExifTag(ExifTagValue.TargetPrinter); + + /// + /// Gets the ImageID exif tag. + /// + public static ExifTag ImageID { get; } = new ExifTag(ExifTagValue.ImageID); + + /// + /// Gets the MDLabName exif tag. + /// + public static ExifTag MDLabName { get; } = new ExifTag(ExifTagValue.MDLabName); + + /// + /// Gets the MDSampleInfo exif tag. + /// + public static ExifTag MDSampleInfo { get; } = new ExifTag(ExifTagValue.MDSampleInfo); + + /// + /// Gets the MDPrepDate exif tag. + /// + public static ExifTag MDPrepDate { get; } = new ExifTag(ExifTagValue.MDPrepDate); + + /// + /// Gets the MDPrepTime exif tag. + /// + public static ExifTag MDPrepTime { get; } = new ExifTag(ExifTagValue.MDPrepTime); + + /// + /// Gets the MDFileUnits exif tag. + /// + public static ExifTag MDFileUnits => new ExifTag(ExifTagValue.MDFileUnits); + + /// + /// Gets the SEMInfo exif tag. + /// + public static ExifTag SEMInfo { get; } = new ExifTag(ExifTagValue.SEMInfo); + + /// + /// Gets the SpectralSensitivity exif tag. + /// + public static ExifTag SpectralSensitivity { get; } = new ExifTag(ExifTagValue.SpectralSensitivity); + + /// + /// Gets the DateTimeOriginal exif tag. + /// + public static ExifTag DateTimeOriginal { get; } = new ExifTag(ExifTagValue.DateTimeOriginal); + + /// + /// Gets the DateTimeDigitized exif tag. + /// + public static ExifTag DateTimeDigitized { get; } = new ExifTag(ExifTagValue.DateTimeDigitized); + + /// + /// Gets the SubsecTime exif tag. + /// + public static ExifTag SubsecTime { get; } = new ExifTag(ExifTagValue.SubsecTime); + + /// + /// Gets the SubsecTimeOriginal exif tag. + /// + public static ExifTag SubsecTimeOriginal { get; } = new ExifTag(ExifTagValue.SubsecTimeOriginal); + + /// + /// Gets the SubsecTimeDigitized exif tag. + /// + public static ExifTag SubsecTimeDigitized { get; } = new ExifTag(ExifTagValue.SubsecTimeDigitized); + + /// + /// Gets the RelatedSoundFile exif tag. + /// + public static ExifTag RelatedSoundFile { get; } = new ExifTag(ExifTagValue.RelatedSoundFile); + + /// + /// Gets the FaxSubaddress exif tag. + /// + public static ExifTag FaxSubaddress { get; } = new ExifTag(ExifTagValue.FaxSubaddress); + + /// + /// Gets the OffsetTime exif tag. + /// + public static ExifTag OffsetTime { get; } = new ExifTag(ExifTagValue.OffsetTime); + + /// + /// Gets the OffsetTimeOriginal exif tag. + /// + public static ExifTag OffsetTimeOriginal { get; } = new ExifTag(ExifTagValue.OffsetTimeOriginal); + + /// + /// Gets the OffsetTimeDigitized exif tag. + /// + public static ExifTag OffsetTimeDigitized { get; } = new ExifTag(ExifTagValue.OffsetTimeDigitized); + + /// + /// Gets the SecurityClassification exif tag. + /// + public static ExifTag SecurityClassification { get; } = new ExifTag(ExifTagValue.SecurityClassification); + + /// + /// Gets the ImageHistory exif tag. + /// + public static ExifTag ImageHistory { get; } = new ExifTag(ExifTagValue.ImageHistory); + + /// + /// Gets the ImageUniqueID exif tag. + /// + public static ExifTag ImageUniqueID { get; } = new ExifTag(ExifTagValue.ImageUniqueID); + + /// + /// Gets the OwnerName exif tag. + /// + public static ExifTag OwnerName { get; } = new ExifTag(ExifTagValue.OwnerName); + + /// + /// Gets the SerialNumber exif tag. + /// + public static ExifTag SerialNumber { get; } = new ExifTag(ExifTagValue.SerialNumber); + + /// + /// Gets the LensMake exif tag. + /// + public static ExifTag LensMake { get; } = new ExifTag(ExifTagValue.LensMake); + + /// + /// Gets the LensModel exif tag. + /// + public static ExifTag LensModel { get; } = new ExifTag(ExifTagValue.LensModel); + + /// + /// Gets the LensSerialNumber exif tag. + /// + public static ExifTag LensSerialNumber { get; } = new ExifTag(ExifTagValue.LensSerialNumber); + + /// + /// Gets the GDALMetadata exif tag. + /// + public static ExifTag GDALMetadata { get; } = new ExifTag(ExifTagValue.GDALMetadata); + + /// + /// Gets the GDALNoData exif tag. + /// + public static ExifTag GDALNoData { get; } = new ExifTag(ExifTagValue.GDALNoData); + + /// + /// Gets the GPSLatitudeRef exif tag. + /// + public static ExifTag GPSLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSLatitudeRef); + + /// + /// Gets the GPSLongitudeRef exif tag. + /// + public static ExifTag GPSLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSLongitudeRef); + + /// + /// Gets the GPSSatellites exif tag. + /// + public static ExifTag GPSSatellites { get; } = new ExifTag(ExifTagValue.GPSSatellites); + + /// + /// Gets the GPSStatus exif tag. + /// + public static ExifTag GPSStatus { get; } = new ExifTag(ExifTagValue.GPSStatus); + + /// + /// Gets the GPSMeasureMode exif tag. + /// + public static ExifTag GPSMeasureMode { get; } = new ExifTag(ExifTagValue.GPSMeasureMode); + + /// + /// Gets the GPSSpeedRef exif tag. + /// + public static ExifTag GPSSpeedRef { get; } = new ExifTag(ExifTagValue.GPSSpeedRef); + + /// + /// Gets the GPSTrackRef exif tag. + /// + public static ExifTag GPSTrackRef { get; } = new ExifTag(ExifTagValue.GPSTrackRef); + + /// + /// Gets the GPSImgDirectionRef exif tag. + /// + public static ExifTag GPSImgDirectionRef { get; } = new ExifTag(ExifTagValue.GPSImgDirectionRef); + + /// + /// Gets the GPSMapDatum exif tag. + /// + public static ExifTag GPSMapDatum { get; } = new ExifTag(ExifTagValue.GPSMapDatum); + + /// + /// Gets the GPSDestLatitudeRef exif tag. + /// + public static ExifTag GPSDestLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLatitudeRef); + + /// + /// Gets the GPSDestLongitudeRef exif tag. + /// + public static ExifTag GPSDestLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLongitudeRef); + + /// + /// Gets the GPSDestBearingRef exif tag. + /// + public static ExifTag GPSDestBearingRef { get; } = new ExifTag(ExifTagValue.GPSDestBearingRef); + + /// + /// Gets the GPSDestDistanceRef exif tag. + /// + public static ExifTag GPSDestDistanceRef { get; } = new ExifTag(ExifTagValue.GPSDestDistanceRef); + + /// + /// Gets the GPSDateStamp exif tag. + /// + public static ExifTag GPSDateStamp { get; } = new ExifTag(ExifTagValue.GPSDateStamp); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs new file mode 100644 index 0000000000..5f48412263 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Undefined.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the JPEGTables exif tag. + /// + public static ExifTag JPEGTables { get; } = new ExifTag(ExifTagValue.JPEGTables); + + /// + /// Gets the OECF exif tag. + /// + public static ExifTag OECF { get; } = new ExifTag(ExifTagValue.OECF); + + /// + /// Gets the ExifVersion exif tag. + /// + public static ExifTag ExifVersion { get; } = new ExifTag(ExifTagValue.ExifVersion); + + /// + /// Gets the ComponentsConfiguration exif tag. + /// + public static ExifTag ComponentsConfiguration { get; } = new ExifTag(ExifTagValue.ComponentsConfiguration); + + /// + /// Gets the MakerNote exif tag. + /// + public static ExifTag MakerNote { get; } = new ExifTag(ExifTagValue.MakerNote); + + /// + /// Gets the UserComment exif tag. + /// + public static ExifTag UserComment { get; } = new ExifTag(ExifTagValue.UserComment); + + /// + /// Gets the FlashpixVersion exif tag. + /// + public static ExifTag FlashpixVersion { get; } = new ExifTag(ExifTagValue.FlashpixVersion); + + /// + /// Gets the SpatialFrequencyResponse exif tag. + /// + public static ExifTag SpatialFrequencyResponse { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse); + + /// + /// Gets the SpatialFrequencyResponse2 exif tag. + /// + public static ExifTag SpatialFrequencyResponse2 { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse2); + + /// + /// Gets the Noise exif tag. + /// + public static ExifTag Noise { get; } = new ExifTag(ExifTagValue.Noise); + + /// + /// Gets the CFAPattern exif tag. + /// + public static ExifTag CFAPattern { get; } = new ExifTag(ExifTagValue.CFAPattern); + + /// + /// Gets the DeviceSettingDescription exif tag. + /// + public static ExifTag DeviceSettingDescription { get; } = new ExifTag(ExifTagValue.DeviceSettingDescription); + + /// + /// Gets the ImageSourceData exif tag. + /// + public static ExifTag ImageSourceData { get; } = new ExifTag(ExifTagValue.ImageSourceData); + + /// + /// Gets the GPSProcessingMethod exif tag. + /// + public static ExifTag GPSProcessingMethod { get; } = new ExifTag(ExifTagValue.GPSProcessingMethod); + + /// + /// Gets the GPSAreaInformation exif tag. + /// + public static ExifTag GPSAreaInformation { get; } = new ExifTag(ExifTagValue.GPSAreaInformation); + + /// + /// Gets the FileSource exif tag. + /// + public static ExifTag FileSource { get; } = new ExifTag(ExifTagValue.FileSource); + + /// + /// Gets the ImageDescription exif tag. + /// + public static ExifTag SceneType { get; } = new ExifTag(ExifTagValue.SceneType); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs new file mode 100644 index 0000000000..65e0314626 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// Class that represents an exif tag from the Exif standard 2.31. + /// + public abstract partial class ExifTag : IEquatable + { + private readonly ushort value; + + internal ExifTag(ushort value) => this.value = value; + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator ushort(ExifTag tag) => tag?.value ?? (ushort)ExifTagValue.Unknown; + + /// + /// Determines whether the specified instances are considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator ==(ExifTag left, ExifTag right) => Equals(left, right); + + /// + /// Determines whether the specified instances are not considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator !=(ExifTag left, ExifTag right) => !Equals(left, right); + + /// + public override bool Equals(object obj) + { + if (obj is ExifTag value) + { + return this.Equals(value); + } + + return false; + } + + /// + public bool Equals(ExifTag other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return this.value == other.value; + } + + /// + public override int GetHashCode() => this.value.GetHashCode(); + + /// + public override string ToString() => ((ExifTagValue)this.value).ToString(); + } +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs similarity index 99% rename from src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs rename to src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs index ddd4591fac..f70bcea37a 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTagValue.cs @@ -1,13 +1,12 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { /// - /// All exif tags from the Exif standard 2.2 - /// Descriptions from: + /// All exif tags from the Exif standard 2.31. /// - public enum ExifTag + internal enum ExifTagValue { /// /// Unknown @@ -1357,9 +1356,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif SerialNumber = 0xA431, /// - /// LensInfo + /// LensSpecification /// - LensInfo = 0xA432, + LensSpecification = 0xA432, /// /// LensMake @@ -1539,6 +1538,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// /// GPSDifferential /// - GPSDifferential = 0x001E + GPSDifferential = 0x001E, } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag{TValueType}.cs new file mode 100644 index 0000000000..5a674277a2 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag{TValueType}.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// Class that represents an exif tag from the Exif standard 2.31 with as the data type of the tag. + /// + /// The data type of the tag. + public sealed class ExifTag : ExifTag + { + internal ExifTag(ExifTagValue value) + : base((ushort)value) + { + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/UnkownExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/UnkownExifTag.cs new file mode 100644 index 0000000000..8f0b36638c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/UnkownExifTag.cs @@ -0,0 +1,13 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class UnkownExifTag : ExifTag + { + internal UnkownExifTag(ExifTagValue value) + : base((ushort)value) + { + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs new file mode 100644 index 0000000000..263bf09348 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifArrayValue{TValueType}.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal abstract class ExifArrayValue : ExifValue, IExifValue + { + protected ExifArrayValue(ExifTag tag) + : base(tag) + { + } + + protected ExifArrayValue(ExifTagValue tag) + : base(tag) + { + } + + internal ExifArrayValue(ExifArrayValue value) + : base(value) + { + } + + public override bool IsArray => true; + + public TValueType[] Value { get; set; } + + public override object GetValue() => this.Value; + + public override bool TrySetValue(object value) + { + if (value is null) + { + this.Value = null; + return true; + } + + Type type = value.GetType(); + if (value.GetType() == typeof(TValueType[])) + { + this.Value = (TValueType[])value; + return true; + } + + if (type == typeof(TValueType)) + { + this.Value = new TValueType[] { (TValueType)value }; + return true; + } + + return false; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs new file mode 100644 index 0000000000..184f4a07c4 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByte.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifByte : ExifValue + { + public ExifByte(ExifTag tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + public ExifByte(ExifTagValue tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + private ExifByte(ExifByte value) + : base(value) => this.DataType = value.DataType; + + public override ExifDataType DataType { get; } + + protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= byte.MinValue && intValue <= byte.MaxValue) + { + this.Value = (byte)intValue; + return true; + } + + return false; + default: + return base.TrySetValue(value); + } + } + + public override IExifValue DeepClone() => new ExifByte(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs new file mode 100644 index 0000000000..854eafc767 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifByteArray.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifByteArray : ExifArrayValue + { + public ExifByteArray(ExifTag tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + public ExifByteArray(ExifTagValue tag, ExifDataType dataType) + : base(tag) => this.DataType = dataType; + + private ExifByteArray(ExifByteArray value) + : base(value) => this.DataType = value.DataType; + + public override ExifDataType DataType { get; } + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is int[] intArrayValue) + { + return this.TrySetSignedIntArray(intArrayValue); + } + + if (value is int intValue) + { + if (intValue >= byte.MinValue && intValue <= byte.MaxValue) + { + this.Value = new byte[] { (byte)intValue }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifByteArray(this); + + private bool TrySetSignedIntArray(int[] intArrayValue) + { + if (Array.FindIndex(intArrayValue, x => x < byte.MinValue || x > byte.MaxValue) > -1) + { + return false; + } + + var value = new byte[intArrayValue.Length]; + for (int i = 0; i < intArrayValue.Length; i++) + { + int s = intArrayValue[i]; + value[i] = (byte)s; + } + + this.Value = value; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs new file mode 100644 index 0000000000..0af5f47ce2 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDouble.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifDouble : ExifValue + { + public ExifDouble(ExifTag tag) + : base(tag) + { + } + + public ExifDouble(ExifTagValue tag) + : base(tag) + { + } + + private ExifDouble(ExifDouble value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.DoubleFloat; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + this.Value = intValue; + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifDouble(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDoubleArray.cs new file mode 100644 index 0000000000..259d5c98a2 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifDoubleArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifDoubleArray : ExifArrayValue + { + public ExifDoubleArray(ExifTag tag) + : base(tag) + { + } + + public ExifDoubleArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifDoubleArray(ExifDoubleArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.DoubleFloat; + + public override IExifValue DeepClone() => new ExifDoubleArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs new file mode 100644 index 0000000000..8d6c41f58e --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloat.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifFloat : ExifValue + { + public ExifFloat(ExifTagValue tag) + : base(tag) + { + } + + private ExifFloat(ExifFloat value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SingleFloat; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + this.Value = intValue; + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifFloat(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloatArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloatArray.cs new file mode 100644 index 0000000000..7789bc3b5d --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifFloatArray.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifFloatArray : ExifArrayValue + { + public ExifFloatArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifFloatArray(ExifFloatArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SingleFloat; + + public override IExifValue DeepClone() => new ExifFloatArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs new file mode 100644 index 0000000000..7f2f631a97 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLong.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLong : ExifValue + { + public ExifLong(ExifTag tag) + : base(tag) + { + } + + public ExifLong(ExifTagValue tag) + : base(tag) + { + } + + private ExifLong(ExifLong value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= uint.MinValue) + { + this.Value = (uint)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifLong(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLongArray.cs new file mode 100644 index 0000000000..e05f50beee --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifLongArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifLongArray : ExifArrayValue + { + public ExifLongArray(ExifTag tag) + : base(tag) + { + } + + public ExifLongArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifLongArray(ExifLongArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Long; + + public override IExifValue DeepClone() => new ExifLongArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs new file mode 100644 index 0000000000..ef7d20c855 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumber.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifNumber : ExifValue + { + public ExifNumber(ExifTag tag) + : base(tag) + { + } + + private ExifNumber(ExifNumber value) + : base(value) + { + } + + public override ExifDataType DataType + { + get + { + if (this.Value > ushort.MaxValue) + { + return ExifDataType.Long; + } + + return ExifDataType.Short; + } + } + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= uint.MinValue) + { + this.Value = (uint)intValue; + return true; + } + + return false; + case uint uintValue: + this.Value = uintValue; + return true; + case short shortValue: + if (shortValue >= uint.MinValue) + { + this.Value = (uint)shortValue; + return true; + } + + return false; + case ushort ushortValue: + this.Value = ushortValue; + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifNumber(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs new file mode 100644 index 0000000000..521cfc0857 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifNumberArray.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifNumberArray : ExifArrayValue + { + public ExifNumberArray(ExifTag tag) + : base(tag) + { + } + + private ExifNumberArray(ExifNumberArray value) + : base(value) + { + } + + public override ExifDataType DataType + { + get + { + if (this.Value is null) + { + return ExifDataType.Short; + } + + for (int i = 0; i < this.Value.Length; i++) + { + if (this.Value[i] > ushort.MaxValue) + { + return ExifDataType.Long; + } + } + + return ExifDataType.Short; + } + } + + public override IExifValue DeepClone() => new ExifNumberArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs new file mode 100644 index 0000000000..3ab77ab321 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRational.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifRational : ExifValue + { + public ExifRational(ExifTag tag) + : base(tag) + { + } + + public ExifRational(ExifTagValue tag) + : base(tag) + { + } + + private ExifRational(ExifRational value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Rational; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case SignedRational signed: + + if (signed.Numerator >= uint.MinValue && signed.Denominator >= uint.MinValue) + { + this.Value = new Rational((uint)signed.Numerator, (uint)signed.Denominator); + } + + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifRational(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs new file mode 100644 index 0000000000..f78e363da4 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifRationalArray.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifRationalArray : ExifArrayValue + { + public ExifRationalArray(ExifTag tag) + : base(tag) + { + } + + public ExifRationalArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifRationalArray(ExifRationalArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Rational; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is SignedRational[] signedArray) + { + return this.TrySetSignedArray(signedArray); + } + + if (value is SignedRational signed) + { + if (signed.Numerator >= 0 && signed.Denominator >= 0) + { + this.Value = new[] { new Rational((uint)signed.Numerator, (uint)signed.Denominator) }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifRationalArray(this); + + private bool TrySetSignedArray(SignedRational[] signed) + { + if (Array.FindIndex(signed, x => x.Numerator < 0 || x.Denominator < 0) > -1) + { + return false; + } + + var unsigned = new Rational[signed.Length]; + for (int i = 0; i < signed.Length; i++) + { + SignedRational s = signed[i]; + unsigned[i] = new Rational((uint)s.Numerator, (uint)s.Denominator); + } + + this.Value = unsigned; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs new file mode 100644 index 0000000000..b11f3fc9f9 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShort.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifShort : ExifValue + { + public ExifShort(ExifTag tag) + : base(tag) + { + } + + public ExifShort(ExifTagValue tag) + : base(tag) + { + } + + private ExifShort(ExifShort value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Short; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= ushort.MinValue && intValue <= ushort.MaxValue) + { + this.Value = (ushort)intValue; + return true; + } + + return false; + case short shortValue: + if (shortValue >= ushort.MinValue) + { + this.Value = (ushort)shortValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifShort(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs new file mode 100644 index 0000000000..379338c10f --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifShortArray.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifShortArray : ExifArrayValue + { + public ExifShortArray(ExifTag tag) + : base(tag) + { + } + + public ExifShortArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifShortArray(ExifShortArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Short; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is int[] signedIntArray) + { + return this.TrySetSignedIntArray(signedIntArray); + } + + if (value is short[] signedShortArray) + { + return this.TrySetSignedShortArray(signedShortArray); + } + + if (value is int signedInt) + { + if (signedInt >= ushort.MinValue && signedInt <= ushort.MaxValue) + { + this.Value = new ushort[] { (ushort)signedInt }; + } + + return true; + } + + if (value is short signedShort) + { + if (signedShort >= ushort.MinValue) + { + this.Value = new ushort[] { (ushort)signedShort }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifShortArray(this); + + private bool TrySetSignedIntArray(int[] signed) + { + if (Array.FindIndex(signed, x => x < ushort.MinValue || x > ushort.MaxValue) > -1) + { + return false; + } + + var unsigned = new ushort[signed.Length]; + for (int i = 0; i < signed.Length; i++) + { + int s = signed[i]; + unsigned[i] = (ushort)s; + } + + this.Value = unsigned; + return true; + } + + private bool TrySetSignedShortArray(short[] signed) + { + if (Array.FindIndex(signed, x => x < ushort.MinValue) > -1) + { + return false; + } + + var unsigned = new ushort[signed.Length]; + for (int i = 0; i < signed.Length; i++) + { + short s = signed[i]; + unsigned[i] = (ushort)s; + } + + this.Value = unsigned; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs new file mode 100644 index 0000000000..a9cb013ca5 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByte.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedByte : ExifValue + { + public ExifSignedByte(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedByte(ExifSignedByte value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedByte; + + protected override string StringValue => this.Value.ToString("X2", CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= sbyte.MinValue && intValue <= sbyte.MaxValue) + { + this.Value = (sbyte)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifSignedByte(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByteArray.cs new file mode 100644 index 0000000000..b0d35cc8a8 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedByteArray.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedByteArray : ExifArrayValue + { + public ExifSignedByteArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedByteArray(ExifSignedByteArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedByte; + + public override IExifValue DeepClone() => new ExifSignedByteArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong.cs new file mode 100644 index 0000000000..c1e6808bf4 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLong.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLong : ExifValue + { + public ExifSignedLong(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLong(ExifSignedLong value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override IExifValue DeepClone() => new ExifSignedLong(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLongArray.cs new file mode 100644 index 0000000000..36d4c00077 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedLongArray.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedLongArray : ExifArrayValue + { + public ExifSignedLongArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedLongArray(ExifSignedLongArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedLong; + + public override IExifValue DeepClone() => new ExifSignedLongArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRational.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRational.cs new file mode 100644 index 0000000000..61fba979b3 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRational.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedRational : ExifValue + { + internal ExifSignedRational(ExifTag tag) + : base(tag) + { + } + + internal ExifSignedRational(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedRational(ExifSignedRational value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedRational; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override IExifValue DeepClone() => new ExifSignedRational(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRationalArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRationalArray.cs new file mode 100644 index 0000000000..2545bd9b27 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedRationalArray.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedRationalArray : ExifArrayValue + { + public ExifSignedRationalArray(ExifTag tag) + : base(tag) + { + } + + public ExifSignedRationalArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedRationalArray(ExifSignedRationalArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedRational; + + public override IExifValue DeepClone() => new ExifSignedRationalArray(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs new file mode 100644 index 0000000000..e00f5c085b --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShort.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedShort : ExifValue + { + public ExifSignedShort(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedShort(ExifSignedShort value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedShort; + + protected override string StringValue => this.Value.ToString(CultureInfo.InvariantCulture); + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + if (intValue >= short.MinValue && intValue <= short.MaxValue) + { + this.Value = (short)intValue; + return true; + } + + return false; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifSignedShort(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs new file mode 100644 index 0000000000..403a501863 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifSignedShortArray.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifSignedShortArray : ExifArrayValue + { + public ExifSignedShortArray(ExifTagValue tag) + : base(tag) + { + } + + private ExifSignedShortArray(ExifSignedShortArray value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.SignedShort; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is int[] intArray) + { + return this.TrySetSignedArray(intArray); + } + + if (value is int intValue) + { + if (intValue >= short.MinValue && intValue <= short.MaxValue) + { + this.Value = new short[] { (short)intValue }; + } + + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifSignedShortArray(this); + + private bool TrySetSignedArray(int[] intArray) + { + if (Array.FindIndex(intArray, x => x < short.MinValue || x > short.MaxValue) > -1) + { + return false; + } + + var value = new short[intArray.Length]; + for (int i = 0; i < intArray.Length; i++) + { + int s = intArray[i]; + value[i] = (short)s; + } + + this.Value = value; + return true; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs new file mode 100644 index 0000000000..0678bc3e41 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifString.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Globalization; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifString : ExifValue + { + public ExifString(ExifTag tag) + : base(tag) + { + } + + public ExifString(ExifTagValue tag) + : base(tag) + { + } + + private ExifString(ExifString value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Ascii; + + protected override string StringValue => this.Value; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + switch (value) + { + case int intValue: + this.Value = intValue.ToString(CultureInfo.InvariantCulture); + return true; + default: + return false; + } + } + + public override IExifValue DeepClone() => new ExifString(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs new file mode 100644 index 0000000000..547e099c92 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal abstract class ExifValue : IExifValue, IEquatable + { + protected ExifValue(ExifTag tag) => this.Tag = tag; + + protected ExifValue(ExifTagValue tag) => this.Tag = new UnkownExifTag(tag); + + internal ExifValue(ExifValue other) + { + Guard.NotNull(other, nameof(other)); + + this.DataType = other.DataType; + this.IsArray = other.IsArray; + this.Tag = other.Tag; + + if (!other.IsArray) + { + // All types are value types except for string which is immutable so safe to simply assign. + this.TrySetValue(other.GetValue()); + } + else + { + // All array types are value types so Clone() is sufficient here. + var array = (Array)other.GetValue(); + this.TrySetValue(array.Clone()); + } + } + + public virtual ExifDataType DataType { get; } + + public virtual bool IsArray { get; } + + public ExifTag Tag { get; } + + public static bool operator ==(ExifValue left, ExifTag right) => Equals(left, right); + + public static bool operator !=(ExifValue left, ExifTag right) => !Equals(left, right); + + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is ExifTag tag) + { + return this.Equals(tag); + } + + if (obj is ExifValue value) + { + return this.Tag.Equals(value.Tag) && Equals(this.GetValue(), value.GetValue()); + } + + return false; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(ExifTag other) => this.Tag.Equals(other); + + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashCode.Combine(this.Tag, this.GetValue()); + + public abstract object GetValue(); + + public abstract bool TrySetValue(object value); + + public abstract IExifValue DeepClone(); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs new file mode 100644 index 0000000000..62d3f40ac2 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -0,0 +1,307 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal static partial class ExifValues + { + public static ExifValue Create(ExifTagValue tag) => (ExifValue)CreateValue(tag); + + public static ExifValue Create(ExifTag tag) => (ExifValue)CreateValue((ExifTagValue)(ushort)tag); + + public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, uint numberOfComponents) + { + bool isArray = numberOfComponents != 1; + + switch (dataType) + { + case ExifDataType.Byte: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); + case ExifDataType.DoubleFloat: return isArray ? (ExifValue)new ExifDoubleArray(tag) : new ExifDouble(tag); + case ExifDataType.SingleFloat: return isArray ? (ExifValue)new ExifFloatArray(tag) : new ExifFloat(tag); + case ExifDataType.Long: return isArray ? (ExifValue)new ExifLongArray(tag) : new ExifLong(tag); + case ExifDataType.Rational: return isArray ? (ExifValue)new ExifRationalArray(tag) : new ExifRational(tag); + case ExifDataType.Short: return isArray ? (ExifValue)new ExifShortArray(tag) : new ExifShort(tag); + case ExifDataType.SignedByte: return isArray ? (ExifValue)new ExifSignedByteArray(tag) : new ExifSignedByte(tag); + case ExifDataType.SignedLong: return isArray ? (ExifValue)new ExifSignedLongArray(tag) : new ExifSignedLong(tag); + case ExifDataType.SignedRational: return isArray ? (ExifValue)new ExifSignedRationalArray(tag) : new ExifSignedRational(tag); + case ExifDataType.SignedShort: return isArray ? (ExifValue)new ExifSignedShortArray(tag) : new ExifSignedShort(tag); + case ExifDataType.Ascii: return new ExifString(tag); + case ExifDataType.Undefined: return isArray ? (ExifValue)new ExifByteArray(tag, dataType) : new ExifByte(tag, dataType); + default: return null; + } + } + + private static object CreateValue(ExifTagValue tag) + { + switch (tag) + { + case ExifTagValue.FaxProfile: return new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte); + case ExifTagValue.ModeNumber: return new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte); + case ExifTagValue.GPSAltitudeRef: return new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte); + + case ExifTagValue.ClipPath: return new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte); + case ExifTagValue.VersionYear: return new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte); + case ExifTagValue.XMP: return new ExifByteArray(ExifTag.XMP, ExifDataType.Byte); + case ExifTagValue.CFAPattern2: return new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte); + case ExifTagValue.TIFFEPStandardID: return new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte); + case ExifTagValue.XPTitle: return new ExifByteArray(ExifTag.XPTitle, ExifDataType.Byte); + case ExifTagValue.XPComment: return new ExifByteArray(ExifTag.XPComment, ExifDataType.Byte); + case ExifTagValue.XPAuthor: return new ExifByteArray(ExifTag.XPAuthor, ExifDataType.Byte); + case ExifTagValue.XPKeywords: return new ExifByteArray(ExifTag.XPKeywords, ExifDataType.Byte); + case ExifTagValue.XPSubject: return new ExifByteArray(ExifTag.XPSubject, ExifDataType.Byte); + case ExifTagValue.GPSVersionID: return new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte); + + case ExifTagValue.PixelScale: return new ExifDoubleArray(ExifTag.PixelScale); + case ExifTagValue.IntergraphMatrix: return new ExifDoubleArray(ExifTag.IntergraphMatrix); + case ExifTagValue.ModelTiePoint: return new ExifDoubleArray(ExifTag.ModelTiePoint); + case ExifTagValue.ModelTransform: return new ExifDoubleArray(ExifTag.ModelTransform); + + case ExifTagValue.SubfileType: return new ExifLong(ExifTag.SubfileType); + case ExifTagValue.SubIFDOffset: return new ExifLong(ExifTag.SubIFDOffset); + case ExifTagValue.GPSIFDOffset: return new ExifLong(ExifTag.GPSIFDOffset); + case ExifTagValue.T4Options: return new ExifLong(ExifTag.T4Options); + case ExifTagValue.T6Options: return new ExifLong(ExifTag.T6Options); + case ExifTagValue.XClipPathUnits: return new ExifLong(ExifTag.XClipPathUnits); + case ExifTagValue.YClipPathUnits: return new ExifLong(ExifTag.YClipPathUnits); + case ExifTagValue.ProfileType: return new ExifLong(ExifTag.ProfileType); + case ExifTagValue.CodingMethods: return new ExifLong(ExifTag.CodingMethods); + case ExifTagValue.T82ptions: return new ExifLong(ExifTag.T82ptions); + case ExifTagValue.JPEGInterchangeFormat: return new ExifLong(ExifTag.JPEGInterchangeFormat); + case ExifTagValue.JPEGInterchangeFormatLength: return new ExifLong(ExifTag.JPEGInterchangeFormatLength); + case ExifTagValue.MDFileTag: return new ExifLong(ExifTag.MDFileTag); + case ExifTagValue.StandardOutputSensitivity: return new ExifLong(ExifTag.StandardOutputSensitivity); + case ExifTagValue.RecommendedExposureIndex: return new ExifLong(ExifTag.RecommendedExposureIndex); + case ExifTagValue.ISOSpeed: return new ExifLong(ExifTag.ISOSpeed); + case ExifTagValue.ISOSpeedLatitudeyyy: return new ExifLong(ExifTag.ISOSpeedLatitudeyyy); + case ExifTagValue.ISOSpeedLatitudezzz: return new ExifLong(ExifTag.ISOSpeedLatitudezzz); + case ExifTagValue.FaxRecvParams: return new ExifLong(ExifTag.FaxRecvParams); + case ExifTagValue.FaxRecvTime: return new ExifLong(ExifTag.FaxRecvTime); + case ExifTagValue.ImageNumber: return new ExifLong(ExifTag.ImageNumber); + + case ExifTagValue.FreeOffsets: return new ExifLongArray(ExifTag.FreeOffsets); + case ExifTagValue.FreeByteCounts: return new ExifLongArray(ExifTag.FreeByteCounts); + case ExifTagValue.ColorResponseUnit: return new ExifLongArray(ExifTag.ColorResponseUnit); + case ExifTagValue.TileOffsets: return new ExifLongArray(ExifTag.TileOffsets); + case ExifTagValue.SMinSampleValue: return new ExifLongArray(ExifTag.SMinSampleValue); + case ExifTagValue.SMaxSampleValue: return new ExifLongArray(ExifTag.SMaxSampleValue); + case ExifTagValue.JPEGQTables: return new ExifLongArray(ExifTag.JPEGQTables); + case ExifTagValue.JPEGDCTables: return new ExifLongArray(ExifTag.JPEGDCTables); + case ExifTagValue.JPEGACTables: return new ExifLongArray(ExifTag.JPEGACTables); + case ExifTagValue.StripRowCounts: return new ExifLongArray(ExifTag.StripRowCounts); + case ExifTagValue.IntergraphRegisters: return new ExifLongArray(ExifTag.IntergraphRegisters); + case ExifTagValue.TimeZoneOffset: return new ExifLongArray(ExifTag.TimeZoneOffset); + + case ExifTagValue.ImageWidth: return new ExifNumber(ExifTag.ImageWidth); + case ExifTagValue.ImageLength: return new ExifNumber(ExifTag.ImageLength); + case ExifTagValue.TileWidth: return new ExifNumber(ExifTag.TileWidth); + case ExifTagValue.TileLength: return new ExifNumber(ExifTag.TileLength); + case ExifTagValue.BadFaxLines: return new ExifNumber(ExifTag.BadFaxLines); + case ExifTagValue.ConsecutiveBadFaxLines: return new ExifNumber(ExifTag.ConsecutiveBadFaxLines); + case ExifTagValue.PixelXDimension: return new ExifNumber(ExifTag.PixelXDimension); + case ExifTagValue.PixelYDimension: return new ExifNumber(ExifTag.PixelYDimension); + + case ExifTagValue.StripOffsets: return new ExifNumberArray(ExifTag.StripOffsets); + case ExifTagValue.TileByteCounts: return new ExifNumberArray(ExifTag.TileByteCounts); + case ExifTagValue.ImageLayer: return new ExifNumberArray(ExifTag.ImageLayer); + + case ExifTagValue.XPosition: return new ExifRational(ExifTag.XPosition); + case ExifTagValue.YPosition: return new ExifRational(ExifTag.YPosition); + case ExifTagValue.XResolution: return new ExifRational(ExifTag.XResolution); + case ExifTagValue.YResolution: return new ExifRational(ExifTag.YResolution); + case ExifTagValue.BatteryLevel: return new ExifRational(ExifTag.BatteryLevel); + case ExifTagValue.ExposureTime: return new ExifRational(ExifTag.ExposureTime); + case ExifTagValue.FNumber: return new ExifRational(ExifTag.FNumber); + case ExifTagValue.MDScalePixel: return new ExifRational(ExifTag.MDScalePixel); + case ExifTagValue.CompressedBitsPerPixel: return new ExifRational(ExifTag.CompressedBitsPerPixel); + case ExifTagValue.ApertureValue: return new ExifRational(ExifTag.ApertureValue); + case ExifTagValue.MaxApertureValue: return new ExifRational(ExifTag.MaxApertureValue); + case ExifTagValue.SubjectDistance: return new ExifRational(ExifTag.SubjectDistance); + case ExifTagValue.FocalLength: return new ExifRational(ExifTag.FocalLength); + case ExifTagValue.FlashEnergy2: return new ExifRational(ExifTag.FlashEnergy2); + case ExifTagValue.FocalPlaneXResolution2: return new ExifRational(ExifTag.FocalPlaneXResolution2); + case ExifTagValue.FocalPlaneYResolution2: return new ExifRational(ExifTag.FocalPlaneYResolution2); + case ExifTagValue.ExposureIndex2: return new ExifRational(ExifTag.ExposureIndex2); + case ExifTagValue.Humidity: return new ExifRational(ExifTag.Humidity); + case ExifTagValue.Pressure: return new ExifRational(ExifTag.Pressure); + case ExifTagValue.Acceleration: return new ExifRational(ExifTag.Acceleration); + case ExifTagValue.FlashEnergy: return new ExifRational(ExifTag.FlashEnergy); + case ExifTagValue.FocalPlaneXResolution: return new ExifRational(ExifTag.FocalPlaneXResolution); + case ExifTagValue.FocalPlaneYResolution: return new ExifRational(ExifTag.FocalPlaneYResolution); + case ExifTagValue.ExposureIndex: return new ExifRational(ExifTag.ExposureIndex); + case ExifTagValue.DigitalZoomRatio: return new ExifRational(ExifTag.DigitalZoomRatio); + case ExifTagValue.GPSAltitude: return new ExifRational(ExifTag.GPSAltitude); + case ExifTagValue.GPSDOP: return new ExifRational(ExifTag.GPSDOP); + case ExifTagValue.GPSSpeed: return new ExifRational(ExifTag.GPSSpeed); + case ExifTagValue.GPSTrack: return new ExifRational(ExifTag.GPSTrack); + case ExifTagValue.GPSImgDirection: return new ExifRational(ExifTag.GPSImgDirection); + case ExifTagValue.GPSDestBearing: return new ExifRational(ExifTag.GPSDestBearing); + case ExifTagValue.GPSDestDistance: return new ExifRational(ExifTag.GPSDestDistance); + + case ExifTagValue.WhitePoint: return new ExifRationalArray(ExifTag.WhitePoint); + case ExifTagValue.PrimaryChromaticities: return new ExifRationalArray(ExifTag.PrimaryChromaticities); + case ExifTagValue.YCbCrCoefficients: return new ExifRationalArray(ExifTag.YCbCrCoefficients); + case ExifTagValue.ReferenceBlackWhite: return new ExifRationalArray(ExifTag.ReferenceBlackWhite); + case ExifTagValue.GPSLatitude: return new ExifRationalArray(ExifTag.GPSLatitude); + case ExifTagValue.GPSLongitude: return new ExifRationalArray(ExifTag.GPSLongitude); + case ExifTagValue.GPSTimestamp: return new ExifRationalArray(ExifTag.GPSTimestamp); + case ExifTagValue.GPSDestLatitude: return new ExifRationalArray(ExifTag.GPSDestLatitude); + case ExifTagValue.GPSDestLongitude: return new ExifRationalArray(ExifTag.GPSDestLongitude); + case ExifTagValue.LensSpecification: return new ExifRationalArray(ExifTag.LensSpecification); + + case ExifTagValue.OldSubfileType: return new ExifShort(ExifTag.OldSubfileType); + case ExifTagValue.Compression: return new ExifShort(ExifTag.Compression); + case ExifTagValue.PhotometricInterpretation: return new ExifShort(ExifTag.PhotometricInterpretation); + case ExifTagValue.Thresholding: return new ExifShort(ExifTag.Thresholding); + case ExifTagValue.CellWidth: return new ExifShort(ExifTag.CellWidth); + case ExifTagValue.CellLength: return new ExifShort(ExifTag.CellLength); + case ExifTagValue.FillOrder: return new ExifShort(ExifTag.FillOrder); + case ExifTagValue.Orientation: return new ExifShort(ExifTag.Orientation); + case ExifTagValue.SamplesPerPixel: return new ExifShort(ExifTag.SamplesPerPixel); + case ExifTagValue.PlanarConfiguration: return new ExifShort(ExifTag.PlanarConfiguration); + case ExifTagValue.GrayResponseUnit: return new ExifShort(ExifTag.GrayResponseUnit); + case ExifTagValue.ResolutionUnit: return new ExifShort(ExifTag.ResolutionUnit); + case ExifTagValue.CleanFaxData: return new ExifShort(ExifTag.CleanFaxData); + case ExifTagValue.InkSet: return new ExifShort(ExifTag.InkSet); + case ExifTagValue.NumberOfInks: return new ExifShort(ExifTag.NumberOfInks); + case ExifTagValue.DotRange: return new ExifShort(ExifTag.DotRange); + case ExifTagValue.Indexed: return new ExifShort(ExifTag.Indexed); + case ExifTagValue.OPIProxy: return new ExifShort(ExifTag.OPIProxy); + case ExifTagValue.JPEGProc: return new ExifShort(ExifTag.JPEGProc); + case ExifTagValue.JPEGRestartInterval: return new ExifShort(ExifTag.JPEGRestartInterval); + case ExifTagValue.YCbCrPositioning: return new ExifShort(ExifTag.YCbCrPositioning); + case ExifTagValue.Rating: return new ExifShort(ExifTag.Rating); + case ExifTagValue.RatingPercent: return new ExifShort(ExifTag.RatingPercent); + case ExifTagValue.ExposureProgram: return new ExifShort(ExifTag.ExposureProgram); + case ExifTagValue.Interlace: return new ExifShort(ExifTag.Interlace); + case ExifTagValue.SelfTimerMode: return new ExifShort(ExifTag.SelfTimerMode); + case ExifTagValue.SensitivityType: return new ExifShort(ExifTag.SensitivityType); + case ExifTagValue.MeteringMode: return new ExifShort(ExifTag.MeteringMode); + case ExifTagValue.LightSource: return new ExifShort(ExifTag.LightSource); + case ExifTagValue.FocalPlaneResolutionUnit2: return new ExifShort(ExifTag.FocalPlaneResolutionUnit2); + case ExifTagValue.SensingMethod2: return new ExifShort(ExifTag.SensingMethod2); + case ExifTagValue.Flash: return new ExifShort(ExifTag.Flash); + case ExifTagValue.ColorSpace: return new ExifShort(ExifTag.ColorSpace); + case ExifTagValue.FocalPlaneResolutionUnit: return new ExifShort(ExifTag.FocalPlaneResolutionUnit); + case ExifTagValue.SensingMethod: return new ExifShort(ExifTag.SensingMethod); + case ExifTagValue.CustomRendered: return new ExifShort(ExifTag.CustomRendered); + case ExifTagValue.ExposureMode: return new ExifShort(ExifTag.ExposureMode); + case ExifTagValue.WhiteBalance: return new ExifShort(ExifTag.WhiteBalance); + case ExifTagValue.FocalLengthIn35mmFilm: return new ExifShort(ExifTag.FocalLengthIn35mmFilm); + case ExifTagValue.SceneCaptureType: return new ExifShort(ExifTag.SceneCaptureType); + case ExifTagValue.GainControl: return new ExifShort(ExifTag.GainControl); + case ExifTagValue.Contrast: return new ExifShort(ExifTag.Contrast); + case ExifTagValue.Saturation: return new ExifShort(ExifTag.Saturation); + case ExifTagValue.Sharpness: return new ExifShort(ExifTag.Sharpness); + case ExifTagValue.SubjectDistanceRange: return new ExifShort(ExifTag.SubjectDistanceRange); + case ExifTagValue.GPSDifferential: return new ExifShort(ExifTag.GPSDifferential); + + case ExifTagValue.BitsPerSample: return new ExifShortArray(ExifTag.BitsPerSample); + case ExifTagValue.MinSampleValue: return new ExifShortArray(ExifTag.MinSampleValue); + case ExifTagValue.MaxSampleValue: return new ExifShortArray(ExifTag.MaxSampleValue); + case ExifTagValue.GrayResponseCurve: return new ExifShortArray(ExifTag.GrayResponseCurve); + case ExifTagValue.ColorMap: return new ExifShortArray(ExifTag.ColorMap); + case ExifTagValue.ExtraSamples: return new ExifShortArray(ExifTag.ExtraSamples); + case ExifTagValue.PageNumber: return new ExifShortArray(ExifTag.PageNumber); + case ExifTagValue.TransferFunction: return new ExifShortArray(ExifTag.TransferFunction); + case ExifTagValue.Predictor: return new ExifShortArray(ExifTag.Predictor); + case ExifTagValue.HalftoneHints: return new ExifShortArray(ExifTag.HalftoneHints); + case ExifTagValue.SampleFormat: return new ExifShortArray(ExifTag.SampleFormat); + case ExifTagValue.TransferRange: return new ExifShortArray(ExifTag.TransferRange); + case ExifTagValue.DefaultImageColor: return new ExifShortArray(ExifTag.DefaultImageColor); + case ExifTagValue.JPEGLosslessPredictors: return new ExifShortArray(ExifTag.JPEGLosslessPredictors); + case ExifTagValue.JPEGPointTransforms: return new ExifShortArray(ExifTag.JPEGPointTransforms); + case ExifTagValue.YCbCrSubsampling: return new ExifShortArray(ExifTag.YCbCrSubsampling); + case ExifTagValue.CFARepeatPatternDim: return new ExifShortArray(ExifTag.CFARepeatPatternDim); + case ExifTagValue.IntergraphPacketData: return new ExifShortArray(ExifTag.IntergraphPacketData); + case ExifTagValue.ISOSpeedRatings: return new ExifShortArray(ExifTag.ISOSpeedRatings); + case ExifTagValue.SubjectArea: return new ExifShortArray(ExifTag.SubjectArea); + case ExifTagValue.SubjectLocation: return new ExifShortArray(ExifTag.SubjectLocation); + + case ExifTagValue.ShutterSpeedValue: return new ExifSignedRational(ExifTag.ShutterSpeedValue); + case ExifTagValue.BrightnessValue: return new ExifSignedRational(ExifTag.BrightnessValue); + case ExifTagValue.ExposureBiasValue: return new ExifSignedRational(ExifTag.ExposureBiasValue); + case ExifTagValue.AmbientTemperature: return new ExifSignedRational(ExifTag.AmbientTemperature); + case ExifTagValue.WaterDepth: return new ExifSignedRational(ExifTag.WaterDepth); + case ExifTagValue.CameraElevationAngle: return new ExifSignedRational(ExifTag.CameraElevationAngle); + + case ExifTagValue.Decode: return new ExifSignedRationalArray(ExifTag.Decode); + + case ExifTagValue.ImageDescription: return new ExifString(ExifTag.ImageDescription); + case ExifTagValue.Make: return new ExifString(ExifTag.Make); + case ExifTagValue.Model: return new ExifString(ExifTag.Model); + case ExifTagValue.Software: return new ExifString(ExifTag.Software); + case ExifTagValue.DateTime: return new ExifString(ExifTag.DateTime); + case ExifTagValue.Artist: return new ExifString(ExifTag.Artist); + case ExifTagValue.HostComputer: return new ExifString(ExifTag.HostComputer); + case ExifTagValue.Copyright: return new ExifString(ExifTag.Copyright); + case ExifTagValue.DocumentName: return new ExifString(ExifTag.DocumentName); + case ExifTagValue.PageName: return new ExifString(ExifTag.PageName); + case ExifTagValue.InkNames: return new ExifString(ExifTag.InkNames); + case ExifTagValue.TargetPrinter: return new ExifString(ExifTag.TargetPrinter); + case ExifTagValue.ImageID: return new ExifString(ExifTag.ImageID); + case ExifTagValue.MDLabName: return new ExifString(ExifTag.MDLabName); + case ExifTagValue.MDSampleInfo: return new ExifString(ExifTag.MDSampleInfo); + case ExifTagValue.MDPrepDate: return new ExifString(ExifTag.MDPrepDate); + case ExifTagValue.MDPrepTime: return new ExifString(ExifTag.MDPrepTime); + case ExifTagValue.MDFileUnits: return new ExifString(ExifTag.MDFileUnits); + case ExifTagValue.SEMInfo: return new ExifString(ExifTag.SEMInfo); + case ExifTagValue.SpectralSensitivity: return new ExifString(ExifTag.SpectralSensitivity); + case ExifTagValue.DateTimeOriginal: return new ExifString(ExifTag.DateTimeOriginal); + case ExifTagValue.DateTimeDigitized: return new ExifString(ExifTag.DateTimeDigitized); + case ExifTagValue.SubsecTime: return new ExifString(ExifTag.SubsecTime); + case ExifTagValue.SubsecTimeOriginal: return new ExifString(ExifTag.SubsecTimeOriginal); + case ExifTagValue.SubsecTimeDigitized: return new ExifString(ExifTag.SubsecTimeDigitized); + case ExifTagValue.RelatedSoundFile: return new ExifString(ExifTag.RelatedSoundFile); + case ExifTagValue.FaxSubaddress: return new ExifString(ExifTag.FaxSubaddress); + case ExifTagValue.OffsetTime: return new ExifString(ExifTag.OffsetTime); + case ExifTagValue.OffsetTimeOriginal: return new ExifString(ExifTag.OffsetTimeOriginal); + case ExifTagValue.OffsetTimeDigitized: return new ExifString(ExifTag.OffsetTimeDigitized); + case ExifTagValue.SecurityClassification: return new ExifString(ExifTag.SecurityClassification); + case ExifTagValue.ImageHistory: return new ExifString(ExifTag.ImageHistory); + case ExifTagValue.ImageUniqueID: return new ExifString(ExifTag.ImageUniqueID); + case ExifTagValue.OwnerName: return new ExifString(ExifTag.OwnerName); + case ExifTagValue.SerialNumber: return new ExifString(ExifTag.SerialNumber); + case ExifTagValue.LensMake: return new ExifString(ExifTag.LensMake); + case ExifTagValue.LensModel: return new ExifString(ExifTag.LensModel); + case ExifTagValue.LensSerialNumber: return new ExifString(ExifTag.LensSerialNumber); + case ExifTagValue.GDALMetadata: return new ExifString(ExifTag.GDALMetadata); + case ExifTagValue.GDALNoData: return new ExifString(ExifTag.GDALNoData); + case ExifTagValue.GPSLatitudeRef: return new ExifString(ExifTag.GPSLatitudeRef); + case ExifTagValue.GPSLongitudeRef: return new ExifString(ExifTag.GPSLongitudeRef); + case ExifTagValue.GPSSatellites: return new ExifString(ExifTag.GPSSatellites); + case ExifTagValue.GPSStatus: return new ExifString(ExifTag.GPSStatus); + case ExifTagValue.GPSMeasureMode: return new ExifString(ExifTag.GPSMeasureMode); + case ExifTagValue.GPSSpeedRef: return new ExifString(ExifTag.GPSSpeedRef); + case ExifTagValue.GPSTrackRef: return new ExifString(ExifTag.GPSTrackRef); + case ExifTagValue.GPSImgDirectionRef: return new ExifString(ExifTag.GPSImgDirectionRef); + case ExifTagValue.GPSMapDatum: return new ExifString(ExifTag.GPSMapDatum); + case ExifTagValue.GPSDestLatitudeRef: return new ExifString(ExifTag.GPSDestLatitudeRef); + case ExifTagValue.GPSDestLongitudeRef: return new ExifString(ExifTag.GPSDestLongitudeRef); + case ExifTagValue.GPSDestBearingRef: return new ExifString(ExifTag.GPSDestBearingRef); + case ExifTagValue.GPSDestDistanceRef: return new ExifString(ExifTag.GPSDestDistanceRef); + case ExifTagValue.GPSDateStamp: return new ExifString(ExifTag.GPSDateStamp); + + case ExifTagValue.FileSource: return new ExifByte(ExifTag.FileSource, ExifDataType.Undefined); + case ExifTagValue.SceneType: return new ExifByte(ExifTag.SceneType, ExifDataType.Undefined); + + case ExifTagValue.JPEGTables: return new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined); + case ExifTagValue.OECF: return new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined); + case ExifTagValue.ExifVersion: return new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined); + case ExifTagValue.ComponentsConfiguration: return new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined); + case ExifTagValue.MakerNote: return new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined); + case ExifTagValue.UserComment: return new ExifByteArray(ExifTag.UserComment, ExifDataType.Undefined); + case ExifTagValue.FlashpixVersion: return new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined); + case ExifTagValue.SpatialFrequencyResponse: return new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined); + case ExifTagValue.SpatialFrequencyResponse2: return new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined); + case ExifTagValue.Noise: return new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined); + case ExifTagValue.CFAPattern: return new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined); + case ExifTagValue.DeviceSettingDescription: return new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined); + case ExifTagValue.ImageSourceData: return new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined); + case ExifTagValue.GPSProcessingMethod: return new ExifByteArray(ExifTag.GPSProcessingMethod, ExifDataType.Undefined); + case ExifTagValue.GPSAreaInformation: return new ExifByteArray(ExifTag.GPSAreaInformation, ExifDataType.Undefined); + + default: return null; + } + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs new file mode 100644 index 0000000000..601630af6b --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValue{TValueType}.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal abstract class ExifValue : ExifValue, IExifValue + { + protected ExifValue(ExifTag tag) + : base(tag) + { + } + + protected ExifValue(ExifTagValue tag) + : base(tag) + { + } + + internal ExifValue(ExifValue value) + : base(value) + { + } + + public TValueType Value { get; set; } + + /// + /// Gets the value of the current instance as a string. + /// + protected abstract string StringValue { get; } + + public override object GetValue() => this.Value; + + public override bool TrySetValue(object value) + { + if (value is null) + { + this.Value = default; + return true; + } + + // We use type comparison here over "is" to avoid compiler optimizations + // that equate short with ushort, and sbyte with byte. + if (value.GetType() == typeof(TValueType)) + { + this.Value = (TValueType)value; + return true; + } + + return false; + } + + public override string ToString() + { + if (this.Value == null) + { + return null; + } + + string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, this.Value); + return description ?? this.StringValue; + } + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs new file mode 100644 index 0000000000..50c4218320 --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// A value of the exif profile. + /// + public interface IExifValue : IDeepCloneable + { + /// + /// Gets the data type of the exif value. + /// + ExifDataType DataType { get; } + + /// + /// Gets a value indicating whether the value is an array. + /// + bool IsArray { get; } + + /// + /// Gets the tag of the exif value. + /// + ExifTag Tag { get; } + + /// + /// Gets the value of this exif value. + /// + /// The value of this exif value. + object GetValue(); + + /// + /// Sets the value of this exif value. + /// + /// The value of this exif value. + /// A value indicating whether the value could be set. + bool TrySetValue(object value); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs new file mode 100644 index 0000000000..72b93ddf9e --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/IExifValue{TValueType}.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + /// A value of the exif profile. + /// + /// The type of the value. + public interface IExifValue : IExifValue + { + /// + /// Gets or sets the value. + /// + TValueType Value { get; set; } + } +} diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs b/src/ImageSharp/Metadata/Profiles/ICC/Curves/IccCurveSegment.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Curves/IccCurveSegment.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/Curves/IccFormulaCurveElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Curves/IccFormulaCurveElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs b/src/ImageSharp/Metadata/Profiles/ICC/Curves/IccOneDimensionalCurve.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Curves/IccOneDimensionalCurve.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs b/src/ImageSharp/Metadata/Profiles/ICC/Curves/IccParametricCurve.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Curves/IccParametricCurve.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/Metadata/Profiles/ICC/Curves/IccResponseCurve.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Curves/IccResponseCurve.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/Curves/IccSampledCurveElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Curves/IccSampledCurveElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Curves.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Curves.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Lut.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Matrix.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.Primitives.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataReader/IccDataReader.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs similarity index 99% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs index b0bd377cb3..585892e96a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs b/src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs rename to src/ImageSharp/Metadata/Profiles/ICC/DataWriter/IccDataWriter.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccClutDataType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccClutDataType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccColorSpaceType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccColorSpaceType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccColorantEncoding.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccColorantEncoding.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccCurveSegmentSignature.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccCurveSegmentSignature.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccDataType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccDataType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccDeviceAttribute.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccDeviceAttribute.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccFormulaCurveType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccMeasurementGeometry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccMeasurementGeometry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccParametricCurveType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccParametricCurveType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccPrimaryPlatformType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccPrimaryPlatformType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccProfileClass.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccProfileClass.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccProfileFlag.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccProfileFlag.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccProfileTag.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccProfileTag.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccRenderingIntent.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccRenderingIntent.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccScreeningFlag.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccScreeningFlag.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccScreeningSpotType.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccScreeningSpotType.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccSignatureName.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccSignatureName.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccStandardIlluminant.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccStandardIlluminant.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccStandardObserver.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccStandardObserver.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs b/src/ImageSharp/Metadata/Profiles/ICC/Enums/IccTypeSignature.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Enums/IccTypeSignature.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs b/src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Exceptions/InvalidIccProfileException.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs rename to src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs rename to src/ImageSharp/Metadata/Profiles/ICC/IccProfileHeader.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs rename to src/ImageSharp/Metadata/Profiles/ICC/IccReader.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/IccTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/Metadata/Profiles/ICC/IccWriter.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs rename to src/ImageSharp/Metadata/Profiles/ICC/IccWriter.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs similarity index 98% rename from src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs index 0d8683397a..668883e1ae 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs +++ b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs @@ -3,8 +3,6 @@ using System; -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs b/src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs rename to src/ImageSharp/Metadata/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccClut.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccColorantTableEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccColorantTableEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLocalizedString.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccLocalizedString.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccLut.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccLut.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccNamedColor.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccNamedColor.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccPositionNumber.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccPositionNumber.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccProfileDescription.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccProfileDescription.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccProfileId.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccProfileId.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccResponseNumber.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccResponseNumber.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccScreeningChannel.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccScreeningChannel.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccTagTableEntry.cs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccVersion.cs b/src/ImageSharp/Metadata/Profiles/ICC/Various/IccVersion.cs similarity index 100% rename from src/ImageSharp/MetaData/Profiles/ICC/Various/IccVersion.cs rename to src/ImageSharp/Metadata/Profiles/ICC/Various/IccVersion.cs diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 21ec2a3fdc..61adedb0d0 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -80,16 +80,28 @@ namespace SixLabors.ImageSharp.PixelFormats void FromBgra32(Bgra32 source); /// - /// Initializes the pixel instance from an value. + /// Initializes the pixel instance from an value. /// - /// The value. - void FromGray8(Gray8 source); + /// The value. + void FromL8(L8 source); /// - /// Initializes the pixel instance from an value. + /// Initializes the pixel instance from an value. /// - /// The value. - void FromGray16(Gray16 source); + /// The value. + void FromL16(L16 source); + + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + void FromLa16(La16 source); + + /// + /// Initializes the pixel instance from an value. + /// + /// The value. + void FromLa32(La32 source); /// /// Initializes the pixel instance from an value. @@ -121,4 +133,4 @@ namespace SixLabors.ImageSharp.PixelFormats /// The value. void FromRgba64(Rgba64 source); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs index 62dc0fcf59..b2f6261ef5 100644 --- a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs +++ b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs @@ -9,17 +9,17 @@ namespace SixLabors.ImageSharp.PixelFormats public enum PixelAlphaCompositionMode { /// - /// returns the destination over the source. + /// Returns the destination over the source. /// SrcOver = 0, /// - /// returns the source colors. + /// Returns the source colors. /// Src, /// - /// returns the source over the destination. + /// Returns the source over the destination. /// SrcAtop, diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index e94ea452be..4075b664ce 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -4,10 +4,6 @@ // using System; using System.Numerics; -using System.Buffers; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { @@ -26,7 +22,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders where TPixel : struct, IPixel { - internal class NormalSrc : PixelBlender + /// + /// A pixel blender that implements the "NormalSrc" composition equation. + /// + public class NormalSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -61,7 +60,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrc : PixelBlender + /// + /// A pixel blender that implements the "MultiplySrc" composition equation. + /// + public class MultiplySrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -96,7 +98,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrc : PixelBlender + /// + /// A pixel blender that implements the "AddSrc" composition equation. + /// + public class AddSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -131,7 +136,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrc : PixelBlender + /// + /// A pixel blender that implements the "SubtractSrc" composition equation. + /// + public class SubtractSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -166,7 +174,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrc : PixelBlender + /// + /// A pixel blender that implements the "ScreenSrc" composition equation. + /// + public class ScreenSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -201,7 +212,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrc : PixelBlender + /// + /// A pixel blender that implements the "DarkenSrc" composition equation. + /// + public class DarkenSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -236,7 +250,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrc : PixelBlender + /// + /// A pixel blender that implements the "LightenSrc" composition equation. + /// + public class LightenSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -271,7 +288,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrc : PixelBlender + /// + /// A pixel blender that implements the "OverlaySrc" composition equation. + /// + public class OverlaySrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -306,7 +326,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrc : PixelBlender + /// + /// A pixel blender that implements the "HardLightSrc" composition equation. + /// + public class HardLightSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -341,7 +364,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "NormalSrcAtop" composition equation. + /// + public class NormalSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -376,7 +402,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcAtop : PixelBlender + /// + /// A pixel blender that implements the "MultiplySrcAtop" composition equation. + /// + public class MultiplySrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -411,7 +440,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "AddSrcAtop" composition equation. + /// + public class AddSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -446,7 +478,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "SubtractSrcAtop" composition equation. + /// + public class SubtractSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -481,7 +516,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "ScreenSrcAtop" composition equation. + /// + public class ScreenSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -516,7 +554,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "DarkenSrcAtop" composition equation. + /// + public class DarkenSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -551,7 +592,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "LightenSrcAtop" composition equation. + /// + public class LightenSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -586,7 +630,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcAtop : PixelBlender + /// + /// A pixel blender that implements the "OverlaySrcAtop" composition equation. + /// + public class OverlaySrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -621,7 +668,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcAtop : PixelBlender + /// + /// A pixel blender that implements the "HardLightSrcAtop" composition equation. + /// + public class HardLightSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -656,7 +706,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcOver : PixelBlender + /// + /// A pixel blender that implements the "NormalSrcOver" composition equation. + /// + public class NormalSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -691,7 +744,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcOver : PixelBlender + /// + /// A pixel blender that implements the "MultiplySrcOver" composition equation. + /// + public class MultiplySrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -726,7 +782,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcOver : PixelBlender + /// + /// A pixel blender that implements the "AddSrcOver" composition equation. + /// + public class AddSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -761,7 +820,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcOver : PixelBlender + /// + /// A pixel blender that implements the "SubtractSrcOver" composition equation. + /// + public class SubtractSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -796,7 +858,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcOver : PixelBlender + /// + /// A pixel blender that implements the "ScreenSrcOver" composition equation. + /// + public class ScreenSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -831,7 +896,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcOver : PixelBlender + /// + /// A pixel blender that implements the "DarkenSrcOver" composition equation. + /// + public class DarkenSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -866,7 +934,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcOver : PixelBlender + /// + /// A pixel blender that implements the "LightenSrcOver" composition equation. + /// + public class LightenSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -901,7 +972,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcOver : PixelBlender + /// + /// A pixel blender that implements the "OverlaySrcOver" composition equation. + /// + public class OverlaySrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -936,7 +1010,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcOver : PixelBlender + /// + /// A pixel blender that implements the "HardLightSrcOver" composition equation. + /// + public class HardLightSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -971,7 +1048,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcIn : PixelBlender + /// + /// A pixel blender that implements the "NormalSrcIn" composition equation. + /// + public class NormalSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1006,7 +1086,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcIn : PixelBlender + /// + /// A pixel blender that implements the "MultiplySrcIn" composition equation. + /// + public class MultiplySrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1041,7 +1124,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcIn : PixelBlender + /// + /// A pixel blender that implements the "AddSrcIn" composition equation. + /// + public class AddSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1076,7 +1162,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcIn : PixelBlender + /// + /// A pixel blender that implements the "SubtractSrcIn" composition equation. + /// + public class SubtractSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1111,7 +1200,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcIn : PixelBlender + /// + /// A pixel blender that implements the "ScreenSrcIn" composition equation. + /// + public class ScreenSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1146,7 +1238,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcIn : PixelBlender + /// + /// A pixel blender that implements the "DarkenSrcIn" composition equation. + /// + public class DarkenSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1181,7 +1276,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcIn : PixelBlender + /// + /// A pixel blender that implements the "LightenSrcIn" composition equation. + /// + public class LightenSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1216,7 +1314,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcIn : PixelBlender + /// + /// A pixel blender that implements the "OverlaySrcIn" composition equation. + /// + public class OverlaySrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1251,7 +1352,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcIn : PixelBlender + /// + /// A pixel blender that implements the "HardLightSrcIn" composition equation. + /// + public class HardLightSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1286,7 +1390,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcOut : PixelBlender + /// + /// A pixel blender that implements the "NormalSrcOut" composition equation. + /// + public class NormalSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1321,7 +1428,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcOut : PixelBlender + /// + /// A pixel blender that implements the "MultiplySrcOut" composition equation. + /// + public class MultiplySrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1356,7 +1466,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcOut : PixelBlender + /// + /// A pixel blender that implements the "AddSrcOut" composition equation. + /// + public class AddSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1391,7 +1504,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcOut : PixelBlender + /// + /// A pixel blender that implements the "SubtractSrcOut" composition equation. + /// + public class SubtractSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1426,7 +1542,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcOut : PixelBlender + /// + /// A pixel blender that implements the "ScreenSrcOut" composition equation. + /// + public class ScreenSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1461,7 +1580,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcOut : PixelBlender + /// + /// A pixel blender that implements the "DarkenSrcOut" composition equation. + /// + public class DarkenSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1496,7 +1618,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcOut : PixelBlender + /// + /// A pixel blender that implements the "LightenSrcOut" composition equation. + /// + public class LightenSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1531,7 +1656,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcOut : PixelBlender + /// + /// A pixel blender that implements the "OverlaySrcOut" composition equation. + /// + public class OverlaySrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1566,7 +1694,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcOut : PixelBlender + /// + /// A pixel blender that implements the "HardLightSrcOut" composition equation. + /// + public class HardLightSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1601,7 +1732,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDest : PixelBlender + /// + /// A pixel blender that implements the "NormalDest" composition equation. + /// + public class NormalDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1636,7 +1770,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDest : PixelBlender + /// + /// A pixel blender that implements the "MultiplyDest" composition equation. + /// + public class MultiplyDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1671,7 +1808,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDest : PixelBlender + /// + /// A pixel blender that implements the "AddDest" composition equation. + /// + public class AddDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1706,7 +1846,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDest : PixelBlender + /// + /// A pixel blender that implements the "SubtractDest" composition equation. + /// + public class SubtractDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1741,7 +1884,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDest : PixelBlender + /// + /// A pixel blender that implements the "ScreenDest" composition equation. + /// + public class ScreenDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1776,7 +1922,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDest : PixelBlender + /// + /// A pixel blender that implements the "DarkenDest" composition equation. + /// + public class DarkenDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1811,7 +1960,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDest : PixelBlender + /// + /// A pixel blender that implements the "LightenDest" composition equation. + /// + public class LightenDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1846,7 +1998,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDest : PixelBlender + /// + /// A pixel blender that implements the "OverlayDest" composition equation. + /// + public class OverlayDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1881,7 +2036,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDest : PixelBlender + /// + /// A pixel blender that implements the "HardLightDest" composition equation. + /// + public class HardLightDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1916,7 +2074,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestAtop : PixelBlender + /// + /// A pixel blender that implements the "NormalDestAtop" composition equation. + /// + public class NormalDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -1951,7 +2112,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestAtop : PixelBlender + /// + /// A pixel blender that implements the "MultiplyDestAtop" composition equation. + /// + public class MultiplyDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -1986,7 +2150,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestAtop : PixelBlender + /// + /// A pixel blender that implements the "AddDestAtop" composition equation. + /// + public class AddDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2021,7 +2188,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestAtop : PixelBlender + /// + /// A pixel blender that implements the "SubtractDestAtop" composition equation. + /// + public class SubtractDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2056,7 +2226,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestAtop : PixelBlender + /// + /// A pixel blender that implements the "ScreenDestAtop" composition equation. + /// + public class ScreenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2091,7 +2264,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestAtop : PixelBlender + /// + /// A pixel blender that implements the "DarkenDestAtop" composition equation. + /// + public class DarkenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2126,7 +2302,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestAtop : PixelBlender + /// + /// A pixel blender that implements the "LightenDestAtop" composition equation. + /// + public class LightenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2161,7 +2340,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestAtop : PixelBlender + /// + /// A pixel blender that implements the "OverlayDestAtop" composition equation. + /// + public class OverlayDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2196,7 +2378,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestAtop : PixelBlender + /// + /// A pixel blender that implements the "HardLightDestAtop" composition equation. + /// + public class HardLightDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2231,7 +2416,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestOver : PixelBlender + /// + /// A pixel blender that implements the "NormalDestOver" composition equation. + /// + public class NormalDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2266,7 +2454,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestOver : PixelBlender + /// + /// A pixel blender that implements the "MultiplyDestOver" composition equation. + /// + public class MultiplyDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2301,7 +2492,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestOver : PixelBlender + /// + /// A pixel blender that implements the "AddDestOver" composition equation. + /// + public class AddDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2336,7 +2530,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestOver : PixelBlender + /// + /// A pixel blender that implements the "SubtractDestOver" composition equation. + /// + public class SubtractDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2371,7 +2568,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestOver : PixelBlender + /// + /// A pixel blender that implements the "ScreenDestOver" composition equation. + /// + public class ScreenDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2406,7 +2606,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestOver : PixelBlender + /// + /// A pixel blender that implements the "DarkenDestOver" composition equation. + /// + public class DarkenDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2441,7 +2644,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestOver : PixelBlender + /// + /// A pixel blender that implements the "LightenDestOver" composition equation. + /// + public class LightenDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2476,7 +2682,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestOver : PixelBlender + /// + /// A pixel blender that implements the "OverlayDestOver" composition equation. + /// + public class OverlayDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2511,7 +2720,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestOver : PixelBlender + /// + /// A pixel blender that implements the "HardLightDestOver" composition equation. + /// + public class HardLightDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2546,7 +2758,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestIn : PixelBlender + /// + /// A pixel blender that implements the "NormalDestIn" composition equation. + /// + public class NormalDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2581,7 +2796,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestIn : PixelBlender + /// + /// A pixel blender that implements the "MultiplyDestIn" composition equation. + /// + public class MultiplyDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2616,7 +2834,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestIn : PixelBlender + /// + /// A pixel blender that implements the "AddDestIn" composition equation. + /// + public class AddDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2651,7 +2872,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestIn : PixelBlender + /// + /// A pixel blender that implements the "SubtractDestIn" composition equation. + /// + public class SubtractDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2686,7 +2910,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestIn : PixelBlender + /// + /// A pixel blender that implements the "ScreenDestIn" composition equation. + /// + public class ScreenDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2721,7 +2948,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestIn : PixelBlender + /// + /// A pixel blender that implements the "DarkenDestIn" composition equation. + /// + public class DarkenDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2756,7 +2986,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestIn : PixelBlender + /// + /// A pixel blender that implements the "LightenDestIn" composition equation. + /// + public class LightenDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2791,7 +3024,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestIn : PixelBlender + /// + /// A pixel blender that implements the "OverlayDestIn" composition equation. + /// + public class OverlayDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2826,7 +3062,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestIn : PixelBlender + /// + /// A pixel blender that implements the "HardLightDestIn" composition equation. + /// + public class HardLightDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2861,7 +3100,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestOut : PixelBlender + /// + /// A pixel blender that implements the "NormalDestOut" composition equation. + /// + public class NormalDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -2896,7 +3138,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestOut : PixelBlender + /// + /// A pixel blender that implements the "MultiplyDestOut" composition equation. + /// + public class MultiplyDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -2931,7 +3176,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestOut : PixelBlender + /// + /// A pixel blender that implements the "AddDestOut" composition equation. + /// + public class AddDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -2966,7 +3214,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestOut : PixelBlender + /// + /// A pixel blender that implements the "SubtractDestOut" composition equation. + /// + public class SubtractDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3001,7 +3252,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestOut : PixelBlender + /// + /// A pixel blender that implements the "ScreenDestOut" composition equation. + /// + public class ScreenDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3036,7 +3290,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestOut : PixelBlender + /// + /// A pixel blender that implements the "DarkenDestOut" composition equation. + /// + public class DarkenDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3071,7 +3328,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestOut : PixelBlender + /// + /// A pixel blender that implements the "LightenDestOut" composition equation. + /// + public class LightenDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3106,7 +3366,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestOut : PixelBlender + /// + /// A pixel blender that implements the "OverlayDestOut" composition equation. + /// + public class OverlayDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3141,7 +3404,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestOut : PixelBlender + /// + /// A pixel blender that implements the "HardLightDestOut" composition equation. + /// + public class HardLightDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3176,7 +3442,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalClear : PixelBlender + /// + /// A pixel blender that implements the "NormalClear" composition equation. + /// + public class NormalClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3211,7 +3480,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyClear : PixelBlender + /// + /// A pixel blender that implements the "MultiplyClear" composition equation. + /// + public class MultiplyClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3246,7 +3518,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddClear : PixelBlender + /// + /// A pixel blender that implements the "AddClear" composition equation. + /// + public class AddClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3281,7 +3556,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractClear : PixelBlender + /// + /// A pixel blender that implements the "SubtractClear" composition equation. + /// + public class SubtractClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3316,7 +3594,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenClear : PixelBlender + /// + /// A pixel blender that implements the "ScreenClear" composition equation. + /// + public class ScreenClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3351,7 +3632,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenClear : PixelBlender + /// + /// A pixel blender that implements the "DarkenClear" composition equation. + /// + public class DarkenClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3386,7 +3670,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenClear : PixelBlender + /// + /// A pixel blender that implements the "LightenClear" composition equation. + /// + public class LightenClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3421,7 +3708,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayClear : PixelBlender + /// + /// A pixel blender that implements the "OverlayClear" composition equation. + /// + public class OverlayClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3456,7 +3746,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightClear : PixelBlender + /// + /// A pixel blender that implements the "HardLightClear" composition equation. + /// + public class HardLightClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3491,7 +3784,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalXor : PixelBlender + /// + /// A pixel blender that implements the "NormalXor" composition equation. + /// + public class NormalXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3526,7 +3822,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyXor : PixelBlender + /// + /// A pixel blender that implements the "MultiplyXor" composition equation. + /// + public class MultiplyXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3561,7 +3860,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddXor : PixelBlender + /// + /// A pixel blender that implements the "AddXor" composition equation. + /// + public class AddXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3596,7 +3898,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractXor : PixelBlender + /// + /// A pixel blender that implements the "SubtractXor" composition equation. + /// + public class SubtractXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3631,7 +3936,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenXor : PixelBlender + /// + /// A pixel blender that implements the "ScreenXor" composition equation. + /// + public class ScreenXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3666,7 +3974,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenXor : PixelBlender + /// + /// A pixel blender that implements the "DarkenXor" composition equation. + /// + public class DarkenXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3701,7 +4012,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenXor : PixelBlender + /// + /// A pixel blender that implements the "LightenXor" composition equation. + /// + public class LightenXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3736,7 +4050,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayXor : PixelBlender + /// + /// A pixel blender that implements the "OverlayXor" composition equation. + /// + public class OverlayXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3771,7 +4088,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightXor : PixelBlender + /// + /// A pixel blender that implements the "HardLightXor" composition equation. + /// + public class HardLightXor : PixelBlender { /// /// Gets the static instance of this blender. diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 66a00975e1..ccb98c495e 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -14,10 +14,6 @@ // using System; using System.Numerics; -using System.Buffers; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { @@ -68,9 +64,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders foreach(var blender in blenders) { var blender_composer= $"{blender}{composer}"; - #> - internal class <#= blender_composer#> : PixelBlender + /// + /// A pixel blender that implements the "<#= blender_composer#>" composition equation. + /// + public class <#= blender_composer#> : PixelBlender { /// /// Gets the static instance of this blender. diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 8b92f95c36..4616ce36c6 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -3,8 +3,6 @@ // - -using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -16,7 +14,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders - + /// + /// Returns the result of the "NormalSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -25,6 +29,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "NormalSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -33,6 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Normal(backdrop, source)); } + /// + /// Returns the result of the "NormalSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -41,14 +59,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Normal(backdrop, source)); } + /// + /// Returns the result of the "NormalSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Normal(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "NormalSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -57,12 +89,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "NormalDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "NormalDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -71,6 +117,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Normal(source, backdrop)); } + /// + /// Returns the result of the "NormalDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -79,14 +132,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Normal(source, backdrop)); } + /// + /// Returns the result of the "NormalDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Normal(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "NormalDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -95,6 +162,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "NormalXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -103,6 +177,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "NormalClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -111,6 +192,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "NormalSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -122,6 +211,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -133,6 +230,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -144,6 +249,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -155,6 +268,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -166,6 +287,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -177,6 +306,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -188,6 +325,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -199,6 +344,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -210,6 +363,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -221,6 +382,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -232,6 +401,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -243,6 +420,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -251,6 +435,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "MultiplySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -259,6 +450,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Multiply(backdrop, source)); } + /// + /// Returns the result of the "MultiplySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -267,14 +465,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Multiply(backdrop, source)); } + /// + /// Returns the result of the "MultiplySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Multiply(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "MultiplySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -283,12 +495,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "MultiplyDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "MultiplyDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -297,6 +523,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Multiply(source, backdrop)); } + /// + /// Returns the result of the "MultiplyDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -305,14 +538,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Multiply(source, backdrop)); } + /// + /// Returns the result of the "MultiplyDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Multiply(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "MultiplyDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -321,6 +568,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -329,6 +583,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "MultiplyClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -337,6 +598,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "MultiplySrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -348,6 +617,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -359,6 +636,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -370,6 +655,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -381,6 +674,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -392,6 +693,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -403,6 +712,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -414,6 +731,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -425,6 +750,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -436,6 +769,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -447,6 +788,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -458,6 +807,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -469,6 +826,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -477,6 +841,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "AddSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -485,6 +856,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Add(backdrop, source)); } + /// + /// Returns the result of the "AddSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -493,14 +871,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Add(backdrop, source)); } + /// + /// Returns the result of the "AddSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Add(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -509,12 +901,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "AddDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "AddDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -523,6 +929,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Add(source, backdrop)); } + /// + /// Returns the result of the "AddDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -531,14 +944,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Add(source, backdrop)); } + /// + /// Returns the result of the "AddDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Add(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "AddDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -547,6 +974,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "AddXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -555,6 +989,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "AddClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -563,6 +1004,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "AddSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -574,6 +1023,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -585,6 +1042,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -596,6 +1061,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -607,6 +1080,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -618,6 +1099,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -629,6 +1118,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -640,6 +1137,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -651,6 +1156,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -662,6 +1175,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -673,6 +1194,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -684,6 +1213,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -695,6 +1232,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -703,6 +1247,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "SubtractSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -711,6 +1262,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Subtract(backdrop, source)); } + /// + /// Returns the result of the "SubtractSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -719,14 +1277,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Subtract(backdrop, source)); } + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Subtract(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "SubtractSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -735,12 +1307,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "SubtractDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "SubtractDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -749,6 +1335,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Subtract(source, backdrop)); } + /// + /// Returns the result of the "SubtractDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -757,14 +1350,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Subtract(source, backdrop)); } + /// + /// Returns the result of the "SubtractDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Subtract(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "SubtractDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -773,6 +1380,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "SubtractXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -781,6 +1395,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "SubtractClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -789,6 +1410,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "SubtractSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -800,6 +1429,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -811,6 +1448,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -822,6 +1467,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -833,6 +1486,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -844,6 +1505,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -855,6 +1524,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -866,6 +1543,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -877,6 +1562,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -888,6 +1581,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -899,6 +1600,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -910,6 +1619,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -921,6 +1638,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -929,6 +1653,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "ScreenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -937,6 +1668,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Screen(backdrop, source)); } + /// + /// Returns the result of the "ScreenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -945,14 +1683,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Screen(backdrop, source)); } + /// + /// Returns the result of the "ScreenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Screen(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "ScreenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -961,12 +1713,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "ScreenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "ScreenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -975,6 +1741,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Screen(source, backdrop)); } + /// + /// Returns the result of the "ScreenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -983,14 +1756,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Screen(source, backdrop)); } + /// + /// Returns the result of the "ScreenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Screen(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "ScreenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -999,6 +1786,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "ScreenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1007,6 +1801,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1015,6 +1816,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "ScreenSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1026,6 +1835,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1037,6 +1854,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1048,6 +1873,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1059,6 +1892,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1070,6 +1911,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1081,6 +1930,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1092,6 +1949,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1103,6 +1968,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1114,6 +1987,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1125,6 +2006,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1136,6 +2025,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1147,6 +2044,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1155,6 +2059,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "DarkenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1163,6 +2074,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Darken(backdrop, source)); } + /// + /// Returns the result of the "DarkenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1171,14 +2089,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Darken(backdrop, source)); } + /// + /// Returns the result of the "DarkenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Darken(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "DarkenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1187,12 +2119,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "DarkenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "DarkenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1201,6 +2147,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Darken(source, backdrop)); } + /// + /// Returns the result of the "DarkenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1209,14 +2162,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Darken(source, backdrop)); } + /// + /// Returns the result of the "DarkenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Darken(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "DarkenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1225,6 +2192,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "DarkenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1233,6 +2207,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "DarkenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1241,6 +2222,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "DarkenSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1252,6 +2241,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1263,6 +2260,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1274,6 +2279,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1285,6 +2298,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1296,6 +2317,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1307,6 +2336,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1318,6 +2355,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1329,6 +2374,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1340,6 +2393,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1351,6 +2412,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1362,6 +2431,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1373,6 +2450,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1381,6 +2465,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "LightenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1389,6 +2480,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Lighten(backdrop, source)); } + /// + /// Returns the result of the "LightenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1397,14 +2495,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Lighten(backdrop, source)); } + /// + /// Returns the result of the "LightenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Lighten(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1413,12 +2525,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "LightenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "LightenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1427,6 +2553,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Lighten(source, backdrop)); } + /// + /// Returns the result of the "LightenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1435,14 +2568,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Lighten(source, backdrop)); } + /// + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Lighten(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "LightenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1451,6 +2598,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "LightenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1459,6 +2613,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "LightenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1467,6 +2628,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "LightenSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1478,6 +2647,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1489,6 +2666,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1500,6 +2685,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1511,6 +2704,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1522,6 +2723,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1533,6 +2742,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1544,6 +2761,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1555,6 +2780,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1566,6 +2799,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1577,6 +2818,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1588,6 +2837,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1599,6 +2856,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1607,6 +2871,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1615,6 +2886,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Overlay(backdrop, source)); } + /// + /// Returns the result of the "OverlaySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1623,14 +2901,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Overlay(backdrop, source)); } + /// + /// Returns the result of the "OverlaySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Overlay(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "OverlaySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1639,12 +2931,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "OverlayDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "OverlayDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1653,6 +2959,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Overlay(source, backdrop)); } + /// + /// Returns the result of the "OverlayDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1661,14 +2974,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Overlay(source, backdrop)); } + /// + /// Returns the result of the "OverlayDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Overlay(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "OverlayDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1677,6 +3004,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "OverlayXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1685,6 +3019,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1693,6 +3034,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "OverlaySrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1704,6 +3053,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1715,6 +3072,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1726,6 +3091,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1737,6 +3110,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1748,6 +3129,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1759,6 +3148,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1770,6 +3167,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1781,6 +3186,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1792,6 +3205,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1803,6 +3224,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1814,6 +3243,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1825,6 +3262,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1833,6 +3277,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "HardLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1841,6 +3292,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, HardLight(backdrop, source)); } + /// + /// Returns the result of the "HardLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1849,14 +3307,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, HardLight(backdrop, source)); } + /// + /// Returns the result of the "HardLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, HardLight(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "HardLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1865,12 +3337,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "HardLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "HardLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1879,6 +3365,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, HardLight(source, backdrop)); } + /// + /// Returns the result of the "HardLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1887,14 +3380,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, HardLight(source, backdrop)); } + /// + /// Returns the result of the "HardLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, HardLight(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "HardLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1903,6 +3410,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "HardLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1911,6 +3425,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "HardLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1919,6 +3440,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "HardLightSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1930,6 +3459,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1941,6 +3478,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1952,6 +3497,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1963,6 +3516,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1974,6 +3535,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1985,6 +3554,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1996,6 +3573,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2007,6 +3592,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2018,6 +3611,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2029,6 +3630,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2040,6 +3649,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index f591ee873d..bc4fa17c27 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -17,8 +17,6 @@ // Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when // AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit. #> - -using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -26,9 +24,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { internal static partial class PorterDuffFunctions { - <# void GeneratePixelBlenders(string blender) { #> - + /// + /// Returns the result of the "<#=blender#>Src" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) { @@ -37,6 +40,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "<#=blender#>SrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -45,6 +55,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, <#=blender#>(backdrop, source)); } + /// + /// Returns the result of the "<#=blender#>SrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -53,14 +70,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, <#=blender#>(backdrop, source)); } + /// + /// Returns the result of the "<#=blender#>SrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, <#=blender#>(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>SrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -69,12 +100,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>Dest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "<#=blender#>DestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -83,6 +128,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, <#=blender#>(source, backdrop)); } + /// + /// Returns the result of the "<#=blender#>DestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -91,14 +143,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, <#=blender#>(source, backdrop)); } + /// + /// Returns the result of the "<#=blender#>DestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, <#=blender#>(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "<#=blender#>DestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -107,6 +173,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "<#=blender#>Xor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) { @@ -115,6 +188,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>Clear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) { @@ -127,6 +207,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders <# void GenerateGenericPixelBlender(string blender, string composer) { #> + /// + /// Returns the result of the "<#=blender#><#=composer#>" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel <#=blender#><#=composer#>(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 9111520a02..97b4458aff 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -21,11 +21,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders internal static partial class PorterDuffFunctions { /// - /// Source over backdrop + /// Returns the result of the "Normal" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source) { @@ -33,11 +33,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source multiplied by backdrop + /// Returns the result of the "Multiply" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source) { @@ -45,11 +45,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source added to backdrop + /// Returns the result of the "Add" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source) { @@ -57,11 +57,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source subtracted from backdrop + /// Returns the result of the "Subtract" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Subtract(Vector4 backdrop, Vector4 source) { @@ -69,11 +69,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Complement of source multiplied by the complement of backdrop + /// Returns the result of the "Screen" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source) { @@ -81,11 +81,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Per element, chooses the smallest value of source and backdrop + /// Returns the result of the "Darken" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source) { @@ -93,11 +93,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Per element, chooses the largest value of source and backdrop + /// Returns the result of the "Lighten" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Lighten(Vector4 backdrop, Vector4 source) { @@ -105,11 +105,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Overlays source over backdrop + /// Returns the result of the "Overlay" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Overlay(Vector4 backdrop, Vector4 source) { @@ -121,11 +121,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Hard light effect + /// Returns the result of the "HardLight" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLight(Vector4 backdrop, Vector4 source) { @@ -145,22 +145,29 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float OverlayValueFunction(float backdrop, float source) { - return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); + return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - (2 * (1 - source) * (1 - backdrop)); } + /// + /// Returns the result of the "Over" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 blend) { // calculate weights - float blendW = dst.W * src.W; - float dstW = dst.W - blendW; - float srcW = src.W - blendW; + float blendW = destination.W * source.W; + float dstW = destination.W - blendW; + float srcW = source.W - blendW; // calculate final alpha - float alpha = dstW + srcW + blendW; + float alpha = dstW + source.W; // calculate final color - Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW); + Vector4 color = (destination * dstW) + (source * srcW) + (blend * blendW); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); @@ -169,18 +176,25 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return color; } + /// + /// Returns the result of the "Atop" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 blend) { // calculate weights - float blendW = dst.W * src.W; - float dstW = dst.W - blendW; + float blendW = destination.W * source.W; + float dstW = destination.W - blendW; // calculate final alpha - float alpha = dstW + blendW; + float alpha = destination.W; // calculate final color - Vector4 color = (dst * dstW) + (blend * blendW); + Vector4 color = (destination * dstW) + (blend * blendW); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); @@ -189,38 +203,56 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return color; } + /// + /// Returns the result of the "In" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 In(Vector4 destination, Vector4 source) { - float alpha = dst.W * src.W; + float alpha = destination.W * source.W; - Vector4 color = src * alpha; // premultiply + Vector4 color = source * alpha; // premultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color.W = alpha; return color; } + /// + /// Returns the result of the "Out" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Out(Vector4 dst, Vector4 src) + public static Vector4 Out(Vector4 destination, Vector4 source) { - float alpha = (1 - dst.W) * src.W; + float alpha = (1 - destination.W) * source.W; - Vector4 color = src * alpha; // premultiply + Vector4 color = source * alpha; // premultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color.W = alpha; return color; } + /// + /// Returns the result of the "XOr" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Xor(Vector4 dst, Vector4 src) + public static Vector4 Xor(Vector4 destination, Vector4 source) { - float srcW = 1 - dst.W; - float dstW = 1 - src.W; + float srcW = 1 - destination.W; + float dstW = 1 - source.W; - float alpha = (src.W * srcW) + (dst.W * dstW); - Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW); + float alpha = (source.W * srcW) + (destination.W * dstW); + Vector4 color = (source.W * source * srcW) + (destination.W * destination * dstW); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); @@ -235,4 +267,4 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Vector4.Zero; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 77bee23937..c64fd3a2d9 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,10 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using System.Numerics; - using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats @@ -13,7 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Abstract base class for calling pixel composition functions /// /// The type of the pixel - internal abstract class PixelBlender + public abstract class PixelBlender where TPixel : struct, IPixel { /// @@ -23,64 +22,11 @@ namespace SixLabors.ImageSharp.PixelFormats /// The source color. /// /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// - /// The final pixel value after composition + /// The final pixel value after composition. public abstract TPixel Blend(TPixel background, TPixel source, float amount); - /// - /// Blend 2 rows together. - /// - /// destination span - /// the background span - /// the source span - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - protected abstract void BlendFunction( - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - float amount); - - /// - /// Blend 2 rows together. - /// - /// destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - protected abstract void BlendFunction( - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount); - - /// - /// Blends 2 rows together - /// - /// to use internally - /// the destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - public void Blend( - Configuration configuration, - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount) - { - this.Blend(configuration, destination, background, source, amount); - } - /// /// Blends 2 rows together /// @@ -90,20 +36,20 @@ namespace SixLabors.ImageSharp.PixelFormats /// the background span /// the source span /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, - ReadOnlySpan amount) + float amount) where TPixelSrc : struct, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); using (IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(destination.Length * 3)) @@ -124,6 +70,25 @@ namespace SixLabors.ImageSharp.PixelFormats } } + /// + /// Blends 2 rows together + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) + => this.Blend(configuration, destination, background, source, amount); + /// /// Blends 2 rows together /// @@ -133,20 +98,20 @@ namespace SixLabors.ImageSharp.PixelFormats /// the background span /// the source span /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, - float amount) + ReadOnlySpan amount) where TPixelSrc : struct, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); using (IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(destination.Length * 3)) @@ -166,5 +131,37 @@ namespace SixLabors.ImageSharp.PixelFormats PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } } + + /// + /// Blend 2 rows together. + /// + /// destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount); + + /// + /// Blend 2 rows together. + /// + /// destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index 2f9abce5d8..eb64e532ea 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// knowing the pixel type. /// [Flags] - internal enum PixelConversionModifiers + public enum PixelConversionModifiers { /// /// No special operation is selected diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs similarity index 69% rename from src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/A8.cs index cd864b5455..cf55a22456 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/A8.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -8,56 +8,56 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats { /// - /// Packed pixel type containing a single 8 bit normalized W values. + /// Packed pixel type containing a single 8-bit normalized alpha value. /// /// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form. /// /// - public struct Alpha8 : IPixel, IPackedVector + public struct A8 : IPixel, IPackedVector { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The alpha component. - public Alpha8(byte alpha) => this.PackedValue = alpha; + public A8(byte alpha) => this.PackedValue = alpha; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The alpha component. - public Alpha8(float alpha) => this.PackedValue = Pack(alpha); + public A8(float alpha) => this.PackedValue = Pack(alpha); /// public byte PackedValue { get; set; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the parameter is equal to the parameter; otherwise, false. /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Alpha8 left, Alpha8 right) => left.Equals(right); + public static bool operator ==(A8 left, A8 right) => left.Equals(right); /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is not equal to the parameter; otherwise, false. /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Alpha8 left, Alpha8 right) => !left.Equals(right); + public static bool operator !=(A8 left, A8 right) => !left.Equals(right); /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -93,11 +93,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.PackedValue = byte.MaxValue; + public void FromL8(L8 source) => this.PackedValue = byte.MaxValue; /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.PackedValue = byte.MaxValue; + public void FromL16(L16 source) => this.PackedValue = byte.MaxValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.PackedValue = source.A; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.PackedValue = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -128,21 +136,21 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The object to compare. /// True if the object is equal to the packed vector. - public override bool Equals(object obj) => obj is Alpha8 other && this.Equals(other); + public override bool Equals(object obj) => obj is A8 other && this.Equals(other); /// - /// Compares another Alpha8 packed vector with the packed vector. + /// Compares another A8 packed vector with the packed vector. /// - /// The Alpha8 packed vector to compare. + /// The A8 packed vector to compare. /// True if the packed vectors are equal. [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Alpha8 other) => this.PackedValue.Equals(other.PackedValue); + public bool Equals(A8 other) => this.PackedValue.Equals(other.PackedValue); /// /// Gets a string representation of the packed vector. /// /// A string representation of the packed vector. - public override string ToString() => $"Alpha8({this.PackedValue})"; + public override string ToString() => $"A8({this.PackedValue})"; /// [MethodImpl(InliningOptions.ShortMethod)] @@ -156,4 +164,4 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] private static byte Pack(float alpha) => (byte)Math.Round(alpha.Clamp(0, 1F) * 255F); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 8981c87458..4dc5c9fb56 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; @@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { this.R = source.PackedValue; this.G = source.PackedValue; @@ -239,7 +239,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); this.R = rgb; @@ -248,6 +248,27 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = byte.MaxValue; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) @@ -357,4 +378,4 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = (byte)vector.W; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs index a0b059dfce..1cd0b80274 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { this.R = source.PackedValue; this.G = source.PackedValue; @@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); this.R = rgb; @@ -157,6 +157,25 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = rgb; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) @@ -212,4 +231,4 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => HashCode.Combine(this.R, this.B, this.G); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs index 9e42de388c..4a7bbded92 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -101,11 +101,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -172,4 +180,4 @@ namespace SixLabors.ImageSharp.PixelFormats | ((int)Math.Round(vector.Z * 31F) & 0x1F)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index ea7a961885..e4ae35c261 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { this.R = source.PackedValue; this.G = source.PackedValue; @@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); this.R = rgb; @@ -204,6 +204,27 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = byte.MaxValue; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgba32(Rgba32 source) @@ -283,4 +304,4 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = (byte)vector.W; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs index 6fcac62917..f4479603f5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -104,11 +104,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -161,4 +169,4 @@ namespace SixLabors.ImageSharp.PixelFormats | ((int)Math.Round(vector.Z * 15F) & 0x0F)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs index abb3eb19e5..b3d7015cfd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// /// - public struct Bgra5551 : IPixel, IPackedVector + public partial struct Bgra5551 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.PixelFormats public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -105,11 +105,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -163,4 +171,4 @@ namespace SixLabors.ImageSharp.PixelFormats | (((int)Math.Round(vector.W) & 0x1) << 15)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs index ab8a13c166..6583670f1b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -101,11 +101,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -173,4 +181,4 @@ namespace SixLabors.ImageSharp.PixelFormats return byte4 | byte3 | byte2 | byte1; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs index 5bacc6747d..15b90f02d9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -42,13 +43,13 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) + public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } @@ -134,36 +135,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); dp.FromArgb32(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromArgb32(sp); } @@ -222,6 +259,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromArgb32(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } /// internal override void From( Configuration configuration, @@ -230,6 +285,7 @@ namespace SixLabors.ImageSharp.PixelFormats { PixelOperations.Instance.ToArgb32(configuration, sourcePixels, destinationPixels); } + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs index 4b2b3a02e4..7eb81764cd 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -43,13 +43,13 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) + public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } @@ -91,36 +91,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); dp.FromBgr24(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromBgr24(sp); } @@ -197,6 +233,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromBgr24(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } /// internal override void From( Configuration configuration, diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs index 6074e19778..222ba8f6ea 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -42,13 +43,13 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) + public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } @@ -134,36 +135,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); dp.FromBgra32(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromBgra32(sp); } @@ -222,6 +259,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromBgra32(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } /// internal override void From( Configuration configuration, @@ -230,6 +285,7 @@ namespace SixLabors.ImageSharp.PixelFormats { PixelOperations.Instance.ToBgra32(configuration, sourcePixels, destinationPixels); } + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra5551.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra5551.PixelOperations.Generated.cs new file mode 100644 index 0000000000..ad157b601a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra5551.PixelOperations.Generated.cs @@ -0,0 +1,252 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Bgra5551 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra5551 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra5551 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra5551(sp); + } + } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToBgra5551(configuration, sourcePixels, destinationPixels); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra5551.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra5551.PixelOperations.Generated.tt new file mode 100644 index 0000000000..c4f2fc8bdd --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra5551.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Bgra5551 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Bgra5551"); #> + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L16.PixelOperations.Generated.cs similarity index 52% rename from src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/Generated/L16.PixelOperations.Generated.cs index d37a253097..aaff5a23b7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L16.PixelOperations.Generated.cs @@ -16,15 +16,15 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Provides optimized overrides for bulk operations. /// - public partial struct Gray8 + public partial struct L16 { /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations + internal class PixelOperations : PixelOperations { /// - internal override void FromGray8(Configuration configuration, ReadOnlySpan source, Span destPixels) + internal override void FromL16(Configuration configuration, ReadOnlySpan source, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); @@ -43,155 +43,209 @@ namespace SixLabors.ImageSharp.PixelFormats /// - internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL16(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL16(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); } } /// - internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray8(sp); + dp.FromL16(sp); + } + } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref L16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref L16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL16(sp); } } /// internal override void From( Configuration configuration, ReadOnlySpan sourcePixels, - Span destinationPixels) + Span destinationPixels) { - PixelOperations.Instance.ToGray8(configuration, sourcePixels, destinationPixels); + PixelOperations.Instance.ToL16(configuration, sourcePixels, destinationPixels); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L16.PixelOperations.Generated.tt similarity index 68% rename from src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt rename to src/ImageSharp/PixelFormats/PixelImplementations/Generated/L16.PixelOperations.Generated.tt index ffa15af9dc..937902346f 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L16.PixelOperations.Generated.tt @@ -6,14 +6,14 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Provides optimized overrides for bulk operations. /// - public partial struct Gray8 + public partial struct L16 { /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations + internal class PixelOperations : PixelOperations { - <# GenerateAllDefaultConversionMethods("Gray8"); #> + <# GenerateAllDefaultConversionMethods("L16"); #> } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L8.PixelOperations.Generated.cs similarity index 52% rename from src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/Generated/L8.PixelOperations.Generated.cs index c30dfe0c02..30a338d484 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L8.PixelOperations.Generated.cs @@ -16,15 +16,15 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Provides optimized overrides for bulk operations. /// - public partial struct Gray16 + public partial struct L8 { /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations + internal class PixelOperations : PixelOperations { /// - internal override void FromGray16(Configuration configuration, ReadOnlySpan source, Span destPixels) + internal override void FromL8(Configuration configuration, ReadOnlySpan source, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); @@ -43,155 +43,209 @@ namespace SixLabors.ImageSharp.PixelFormats /// - internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL8(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL8(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); } } /// - internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); - ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); - dp.FromGray16(sp); + dp.FromL8(sp); + } + } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref L8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref L8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromL8(sp); } } /// internal override void From( Configuration configuration, ReadOnlySpan sourcePixels, - Span destinationPixels) + Span destinationPixels) { - PixelOperations.Instance.ToGray16(configuration, sourcePixels, destinationPixels); + PixelOperations.Instance.ToL8(configuration, sourcePixels, destinationPixels); } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L8.PixelOperations.Generated.tt similarity index 68% rename from src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt rename to src/ImageSharp/PixelFormats/PixelImplementations/Generated/L8.PixelOperations.Generated.tt index ac484bdbc0..d2e802a190 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/L8.PixelOperations.Generated.tt @@ -6,14 +6,14 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Provides optimized overrides for bulk operations. /// - public partial struct Gray16 + public partial struct L8 { /// /// Provides optimized overrides for bulk operations. /// - internal class PixelOperations : PixelOperations + internal class PixelOperations : PixelOperations { - <# GenerateAllDefaultConversionMethods("Gray16"); #> + <# GenerateAllDefaultConversionMethods("L8"); #> } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La16.PixelOperations.Generated.cs new file mode 100644 index 0000000000..ee0641aa8c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La16.PixelOperations.Generated.cs @@ -0,0 +1,252 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct La16 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromLa16(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa16(sp); + } + } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToLa16(configuration, sourcePixels, destinationPixels); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La16.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La16.PixelOperations.Generated.tt new file mode 100644 index 0000000000..5d6661631d --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La16.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct La16 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("La16"); #> + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La32.PixelOperations.Generated.cs new file mode 100644 index 0000000000..a95fce7aac --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La32.PixelOperations.Generated.cs @@ -0,0 +1,252 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct La32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromLa32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref La32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromLa32(sp); + } + } + /// + internal override void From( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destinationPixels) + { + PixelOperations.Instance.ToLa32(configuration, sourcePixels, destinationPixels); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La32.PixelOperations.Generated.tt new file mode 100644 index 0000000000..e2fb4867a9 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/La32.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct La32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("La32"); #> + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs index e5f648fcb7..7f03d02b0c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -42,13 +43,13 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) + public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) + public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } @@ -108,36 +109,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgb24(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgb24(sp); } @@ -196,6 +233,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgb24(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } /// internal override void From( Configuration configuration, @@ -204,6 +259,7 @@ namespace SixLabors.ImageSharp.PixelFormats { PixelOperations.Instance.ToRgb24(configuration, sourcePixels, destinationPixels); } + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs index d3b2bc6f48..69e60ac0a8 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -97,36 +98,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgb48(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgb48(sp); } @@ -185,6 +222,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgb48(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } /// internal override void From( Configuration configuration, @@ -193,6 +248,7 @@ namespace SixLabors.ImageSharp.PixelFormats { PixelOperations.Instance.ToRgb48(configuration, sourcePixels, destinationPixels); } + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs index 0da2bf2449..a4d13acd60 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -123,36 +124,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgba32(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgba32(sp); } @@ -211,6 +248,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgba32(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } /// internal override void From( Configuration configuration, @@ -219,6 +274,7 @@ namespace SixLabors.ImageSharp.PixelFormats { PixelOperations.Instance.ToRgba32(configuration, sourcePixels, destinationPixels); } + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs index 83f5cadd6d..e25dd5129a 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -97,36 +98,72 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref L8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + ref L16 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgba64(sp); } } /// - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + ref La16 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + ref La16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref La32 dp = ref Unsafe.Add(ref destRef, i); dp.FromRgba64(sp); } @@ -185,6 +222,24 @@ namespace SixLabors.ImageSharp.PixelFormats dp.FromRgba64(sp); } } + + /// + internal override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra5551 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra5551 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } /// internal override void From( Configuration configuration, @@ -193,6 +248,7 @@ namespace SixLabors.ImageSharp.PixelFormats { PixelOperations.Instance.ToRgba64(configuration, sourcePixels, destinationPixels); } + } } } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude index 17e9469f31..355273c038 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -1,4 +1,4 @@ -<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> @@ -15,7 +15,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; <#+ - static readonly string[] CommonPixelTypes = { "Argb32", "Bgr24", "Bgra32", "Gray8", "Gray16", "Rgb24", "Rgba32", "Rgb48", "Rgba64" }; + static readonly string[] CommonPixelTypes = { "Argb32", "Bgr24", "Bgra32", "L8", "L16", "La16", "La32", "Rgb24", "Rgba32", "Rgb48", "Rgba64", "Bgra5551" }; static readonly string[] Optimized32BitTypes = { "Rgba32", "Argb32", "Bgra32" }; @@ -130,13 +130,13 @@ using System.Runtime.InteropServices; } #> /// - internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) + public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(<#=removeTheseModifiers#>)); } /// - internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors, PixelConversionModifiers modifiers) + public override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(<#=removeTheseModifiers#>)); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs index 580cc5399b..4d6c4985a7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -94,11 +94,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -144,4 +152,4 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => this.PackedValue.GetHashCode(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs index 8854f16501..300458cb2b 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -105,11 +105,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs index 3b30ebd1e6..5ccc09e9f3 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -113,11 +113,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -175,4 +183,4 @@ namespace SixLabors.ImageSharp.PixelFormats return num4 | num3 | num2 | num1; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs similarity index 75% rename from src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/L16.cs index b5bd14d9f1..cbe34745c9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L16.cs @@ -7,48 +7,48 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats { /// - /// Packed pixel type containing a single 16 bit normalized gray values. + /// Packed pixel type containing a single 16-bit normalized luminance value. /// /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// - public partial struct Gray16 : IPixel, IPackedVector + public partial struct L16 : IPixel, IPackedVector { private const float Max = ushort.MaxValue; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The luminance component - public Gray16(ushort luminance) => this.PackedValue = luminance; + public L16(ushort luminance) => this.PackedValue = luminance; /// public ushort PackedValue { get; set; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is equal to the parameter; otherwise, false. /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Gray16 left, Gray16 right) => left.Equals(right); + public static bool operator ==(L16 left, L16 right) => left.Equals(right); /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is not equal to the parameter; otherwise, false. /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Gray16 left, Gray16 right) => !left.Equals(right); + public static bool operator !=(L16 left, L16 right) => !left.Equals(right); /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -106,11 +106,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.PackedValue = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); + public void FromL8(L8 source) => this.PackedValue = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.PackedValue = source.PackedValue; + public void FromL16(L16 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.PackedValue = ImageMaths.UpscaleFrom8BitTo16Bit(source.L); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.PackedValue = source.L; /// [MethodImpl(InliningOptions.ShortMethod)] @@ -152,14 +160,14 @@ namespace SixLabors.ImageSharp.PixelFormats public void FromRgba64(Rgba64 source) => this.PackedValue = ImageMaths.Get16BitBT709Luminance(source.R, source.G, source.B); /// - public override bool Equals(object obj) => obj is Gray16 other && this.Equals(other); + public override bool Equals(object obj) => obj is L16 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Gray16 other) => this.PackedValue.Equals(other.PackedValue); + public bool Equals(L16 other) => this.PackedValue.Equals(other.PackedValue); /// - public override string ToString() => $"Gray16({this.PackedValue})"; + public override string ToString() => $"L16({this.PackedValue})"; /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs similarity index 73% rename from src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/L8.cs index ac67c9d170..b4911ec1c7 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/L8.cs @@ -7,49 +7,49 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats { /// - /// Packed pixel type containing a single 8 bit normalized gray values. + /// Packed pixel type containing a single 8-bit normalized luminance value. /// /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. /// /// - public partial struct Gray8 : IPixel, IPackedVector + public partial struct L8 : IPixel, IPackedVector { private static readonly Vector4 MaxBytes = new Vector4(255F); private static readonly Vector4 Half = new Vector4(0.5F); /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The luminance component. - public Gray8(byte luminance) => this.PackedValue = luminance; + public L8(byte luminance) => this.PackedValue = luminance; /// public byte PackedValue { get; set; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is equal to the parameter; otherwise, false. /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Gray8 left, Gray8 right) => left.Equals(right); + public static bool operator ==(L8 left, L8 right) => left.Equals(right); /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is not equal to the parameter; otherwise, false. /// [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Gray8 left, Gray8 right) => !left.Equals(right); + public static bool operator !=(L8 left, L8 right) => !left.Equals(right); /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -89,11 +89,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.PackedValue = source.PackedValue; + public void FromL8(L8 source) => this = source; /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.PackedValue = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + public void FromL16(L16 source) => this.PackedValue = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.PackedValue = source.L; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.PackedValue = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -130,14 +138,14 @@ namespace SixLabors.ImageSharp.PixelFormats ImageMaths.DownScaleFrom16BitTo8Bit(source.B)); /// - public override bool Equals(object obj) => obj is Gray8 other && this.Equals(other); + public override bool Equals(object obj) => obj is L8 other && this.Equals(other); /// [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Gray8 other) => this.PackedValue.Equals(other.PackedValue); + public bool Equals(L8 other) => this.PackedValue.Equals(other.PackedValue); /// - public override string ToString() => $"Gray8({this.PackedValue})"; + public override string ToString() => $"L8({this.PackedValue})"; /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs new file mode 100644 index 0000000000..2ab5da158e --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La16.cs @@ -0,0 +1,228 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing two 8-bit normalized values representing luminance and alpha. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + [StructLayout(LayoutKind.Explicit)] + public partial struct La16 : IPixel, IPackedVector + { + private static readonly Vector4 MaxBytes = new Vector4(255F); + private static readonly Vector4 Half = new Vector4(0.5F); + + /// + /// Gets or sets the luminance component. + /// + [FieldOffset(0)] + public byte L; + + /// + /// Gets or sets the alpha component. + /// + [FieldOffset(1)] + public byte A; + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component. + /// The alpha componant. + public La16(byte l, byte a) + { + this.L = l; + this.A = a; + } + + /// + public ushort PackedValue + { + get => Unsafe.As(ref this); + + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(La16 left, La16 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(La16 left, La16 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(La16 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override bool Equals(object obj) => obj is La16 other && this.Equals(other); + + /// + public override string ToString() => $"La16({this.L}, {this.A})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.L = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + this.L = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.L = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL16(L16 source) + { + this.L = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL8(L8 source) + { + this.L = source.PackedValue; + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + this.L = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.L = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + { + this.L = ImageMaths.Get8BitBT709Luminance( + ImageMaths.DownScaleFrom16BitTo8Bit(source.R), + ImageMaths.DownScaleFrom16BitTo8Bit(source.G), + ImageMaths.DownScaleFrom16BitTo8Bit(source.B)); + + this.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) + { + this.L = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + this.A = source.A; + } + + /// + public void FromRgba64(Rgba64 source) + { + this.L = ImageMaths.Get8BitBT709Luminance( + ImageMaths.DownScaleFrom16BitTo8Bit(source.R), + ImageMaths.DownScaleFrom16BitTo8Bit(source.G), + ImageMaths.DownScaleFrom16BitTo8Bit(source.B)); + + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.L; + dest.G = this.L; + dest.B = this.L; + dest.A = this.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + const float Max = 255F; + float rgb = this.L / Max; + return new Vector4(rgb, rgb, rgb, this.A / Max); + } + + [MethodImpl(InliningOptions.ShortMethod)] + internal void ConvertFromRgbaScaledVector4(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + this.L = ImageMaths.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); + this.A = (byte)vector.W; + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs new file mode 100644 index 0000000000..b13c43827c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/La32.cs @@ -0,0 +1,243 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing two 16-bit normalized values representing luminance and alpha. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + [StructLayout(LayoutKind.Explicit)] + public partial struct La32 : IPixel, IPackedVector + { + private const float Max = ushort.MaxValue; + + /// + /// Gets or sets the luminance component. + /// + [FieldOffset(0)] + public ushort L; + + /// + /// Gets or sets the alpha component. + /// + [FieldOffset(2)] + public ushort A; + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component. + /// The alpha componant. + public La32(ushort l, ushort a) + { + this.L = l; + this.A = a; + } + + /// + public uint PackedValue + { + get => Unsafe.As(ref this); + + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(La32 left, La32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(La32 left, La32 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(La32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override bool Equals(object obj) => obj is La32 other && this.Equals(other); + + /// + public override string ToString() => $"La32({this.L}, {this.A})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.L = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + this.L = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.L = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL16(L16 source) + { + this.L = source.PackedValue; + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromL8(L8 source) + { + this.L = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.L = ImageMaths.UpscaleFrom8BitTo16Bit(source.L); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.L = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + { + this.L = ImageMaths.Get16BitBT709Luminance(source.R, source.G, source.B); + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) + { + this.L = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) + { + this.L = ImageMaths.Get16BitBT709Luminance(source.R, source.G, source.B); + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(this.L); + dest.R = rgb; + dest.G = rgb; + dest.B = rgb; + dest.A = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + float scaled = this.L / Max; + return new Vector4(scaled, scaled, scaled, this.A / Max); + } + + [MethodImpl(InliningOptions.ShortMethod)] + internal void ConvertFromRgbaScaledVector4(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.L = ImageMaths.Get16BitBT709Luminance( + vector.X, + vector.Y, + vector.Z); + + this.A = (ushort)MathF.Round(vector.W); + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs index a24e725692..d6362dacce 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -115,11 +115,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs index 73a3d32625..f6c5d25800 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -116,11 +116,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -176,4 +184,4 @@ namespace SixLabors.ImageSharp.PixelFormats return byte4 | byte3 | byte2 | byte1; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs index 3ede947139..989c03e22e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -111,11 +111,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs index 453b1c1b7c..ed849a6c79 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -118,11 +118,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -180,4 +188,4 @@ namespace SixLabors.ImageSharp.PixelFormats return word4 | word3 | word2 | word1; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs index 0411f8f3e9..a7385d5af5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -99,11 +99,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -161,4 +169,4 @@ namespace SixLabors.ImageSharp.PixelFormats return (uint)(((int)Math.Round(vector.X) & 0xFFFF) | (((int)Math.Round(vector.Y) & 0xFFFF) << 16)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index 469dbbad4a..65191e86f6 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { this.R = source.PackedValue; this.G = source.PackedValue; @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); this.R = rgb; @@ -172,6 +172,25 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = rgb; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); @@ -242,4 +261,4 @@ namespace SixLabors.ImageSharp.PixelFormats this.B = (byte)vector.Z; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs index f59036ec7d..c78219a484 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { ushort rgb = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); this.R = rgb; @@ -142,13 +142,32 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { this.R = source.PackedValue; this.G = source.PackedValue; this.B = source.PackedValue; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + ushort rgb = ImageMaths.UpscaleFrom8BitTo16Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) @@ -195,4 +214,4 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs index 38f61d56c1..330f5a8ee5 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -105,11 +105,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -164,4 +172,4 @@ namespace SixLabors.ImageSharp.PixelFormats | (((int)Math.Round(vector.W) & 0x03) << 30)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs index da02f374e1..7a12f6823d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.PixelFormats internal partial class PixelOperations : PixelOperations { /// - internal override void ToVector4( + public override void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void FromVector4Destructive( + public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, Span destPixels, @@ -52,4 +52,4 @@ namespace SixLabors.ImageSharp.PixelFormats } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index ba259ca8ed..10631e2cf9 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -307,7 +307,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { this.R = source.PackedValue; this.G = source.PackedValue; @@ -317,7 +317,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); this.R = rgb; @@ -326,6 +326,27 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = byte.MaxValue; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + this.A = source.A; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) @@ -483,4 +504,4 @@ namespace SixLabors.ImageSharp.PixelFormats return new string(new[] { r, r, g, g, b, b, a, a }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs index 978d9b0156..56bc6f4559 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -256,7 +256,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) + public void FromL8(L8 source) { ushort rgb = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); this.R = rgb; @@ -267,7 +267,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) + public void FromL16(L16 source) { this.R = source.PackedValue; this.G = source.PackedValue; @@ -275,6 +275,27 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = ushort.MaxValue; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) + { + ushort rgb = ImageMaths.UpscaleFrom8BitTo16Bit(source.L); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) + { + this.R = source.L; + this.G = source.L; + this.B = source.L; + this.A = source.A; + } + /// [MethodImpl(InliningOptions.ShortMethod)] public void FromRgb24(Rgb24 source) @@ -399,4 +420,4 @@ namespace SixLabors.ImageSharp.PixelFormats [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => this.PackedValue.GetHashCode(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs index b331dbd99f..d19fc7d928 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.PixelFormats internal class PixelOperations : PixelOperations { /// - internal override void FromVector4Destructive( + public override void FromVector4Destructive( Configuration configuration, Span sourceVectors, Span destinationColors, @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - internal override void ToVector4( + public override void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, @@ -46,33 +46,33 @@ namespace SixLabors.ImageSharp.PixelFormats Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers); } - internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); - ref Gray8 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + ref L8 destBaseRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destBaseRef, i); + ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); dp.ConvertFromRgbaScaledVector4(sp); } } - internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + internal override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); - ref Gray16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + ref L16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destBaseRef, i); + ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); dp.ConvertFromRgbaScaledVector4(sp); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 10c642300c..67f09f3a5d 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -140,11 +140,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs index 14987e5829..1cc7d269c4 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -117,11 +117,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs index c52b293476..433f49f153 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -122,11 +122,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] - public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); /// [MethodImpl(InliningOptions.ShortMethod)] @@ -186,4 +194,4 @@ namespace SixLabors.ImageSharp.PixelFormats return word4 | word3 | word2 | word1; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs index 7002819928..1bc237100e 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. // @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -227,153 +227,297 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - /// Converts all pixels in 'source` span of into a span of -s. + /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations - /// The source of data. + /// A to configure internal operations. + /// The source of data. /// The to the destination pixels. - internal virtual void FromGray8(Configuration configuration, ReadOnlySpan source, Span destPixels) + internal virtual void FromL8(Configuration configuration, ReadOnlySpan source, Span destPixels) { Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); - ref Gray8 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref L8 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < source.Length; i++) { - ref Gray8 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref L8 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - dp.FromGray8(sp); + dp.FromL8(sp); } } /// - /// A helper for that expects a byte span. - /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void FromGray8Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + internal void FromL8Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) { - this.FromGray8(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + this.FromL8(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'sourcePixels` span to a span of -s. /// /// A to configure internal operations /// The span of source pixels - /// The destination span of data. - internal virtual void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + /// The destination span of data. + internal virtual void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray8 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + ref L8 destBaseRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Gray8 dp = ref Unsafe.Add(ref destBaseRef, i); + ref L8 dp = ref Unsafe.Add(ref destBaseRef, i); dp.FromScaledVector4(sp.ToScaledVector4()); } } /// - /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. /// /// A to configure internal operations /// The to the source pixels. /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ToGray8Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + internal void ToL8Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { - this.ToGray8(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToL8(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations. + /// The source of data. + /// The to the destination pixels. + internal virtual void FromL16(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref L16 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref L16 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromL16(sp); + } } /// - /// Converts all pixels in 'source` span of into a span of -s. + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations. + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromL16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromL16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. /// /// A to configure internal operations - /// The source of data. + /// The span of source pixels + /// The destination span of data. + internal virtual void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref L16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref L16 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToL16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToL16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations. + /// The source of data. /// The to the destination pixels. - internal virtual void FromGray16(Configuration configuration, ReadOnlySpan source, Span destPixels) + internal virtual void FromLa16(Configuration configuration, ReadOnlySpan source, Span destPixels) { Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); - ref Gray16 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref La16 sourceBaseRef = ref MemoryMarshal.GetReference(source); ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < source.Length; i++) { - ref Gray16 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref La16 sp = ref Unsafe.Add(ref sourceBaseRef, i); ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); - dp.FromGray16(sp); + dp.FromLa16(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations. + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromLa16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromLa16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref La16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref La16 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); } } /// - /// A helper for that expects a byte span. - /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. /// /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToLa16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToLa16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations. + /// The source of data. + /// The to the destination pixels. + internal virtual void FromLa32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref La32 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref La32 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromLa32(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void FromGray16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + internal void FromLa32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) { - this.FromGray16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + this.FromLa32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); } /// - /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// Converts all pixels of the 'sourcePixels` span to a span of -s. /// /// A to configure internal operations /// The span of source pixels - /// The destination span of data. - internal virtual void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + /// The destination span of data. + internal virtual void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Gray16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + ref La32 destBaseRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Gray16 dp = ref Unsafe.Add(ref destBaseRef, i); + ref La32 dp = ref Unsafe.Add(ref destBaseRef, i); dp.FromScaledVector4(sp.ToScaledVector4()); } } /// - /// A helper for that expects a byte span as destination. - /// The layout of the data in 'destBytes' must be compatible with layout. + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. /// /// A to configure internal operations /// The to the source pixels. /// The to the destination bytes. /// The number of pixels to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ToGray16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + internal void ToLa32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { - this.ToGray16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + this.ToLa32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -396,7 +540,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -445,7 +589,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -468,7 +612,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -517,7 +661,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -540,7 +684,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -589,7 +733,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -612,7 +756,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -661,7 +805,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destPixels) @@ -684,7 +828,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -730,4 +874,4 @@ namespace SixLabors.ImageSharp.PixelFormats this.ToBgra5551(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt index 8603012321..a9fe3ea20b 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt @@ -1,4 +1,4 @@ -<# +<# // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. #> @@ -18,7 +18,7 @@ /// /// Converts all pixels in 'source` span of into a span of -s. /// - /// A to configure internal operations + /// A to configure internal operations. /// The source of data. /// The to the destination pixels. internal virtual void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span destPixels) @@ -41,7 +41,7 @@ /// A helper for that expects a byte span. /// The layout of the data in 'sourceBytes' must be compatible with layout. /// - /// A to configure internal operations + /// A to configure internal operations. /// The to the source bytes. /// The to the destination pixels. /// The number of pixels to convert. @@ -118,11 +118,17 @@ namespace SixLabors.ImageSharp.PixelFormats GenerateFromMethods("Bgra32"); GenerateToDestFormatMethods("Bgra32"); - GenerateFromMethods("Gray8"); - GenerateToDestFormatMethods("Gray8"); + GenerateFromMethods("L8"); + GenerateToDestFormatMethods("L8"); + + GenerateFromMethods("L16"); + GenerateToDestFormatMethods("L16"); + + GenerateFromMethods("La16"); + GenerateToDestFormatMethods("La16"); - GenerateFromMethods("Gray16"); - GenerateToDestFormatMethods("Gray16"); + GenerateFromMethods("La32"); + GenerateToDestFormatMethods("La32"); GenerateFromMethods("Rgb24"); GenerateToDestFormatMethods("Rgb24"); @@ -140,4 +146,4 @@ namespace SixLabors.ImageSharp.PixelFormats GenerateToDestFormatMethods("Bgra5551"); #> } -} \ 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 63db674c8e..9f6fd27852 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats.PixelBlenders; @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// the blending and composition to apply /// A . - internal PixelBlender GetPixelBlender(GraphicsOptions options) + public PixelBlender GetPixelBlender(GraphicsOptions options) { return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); } @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The color blending mode to apply /// The alpha composition mode to apply /// A . - internal virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + public virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) { switch (alphaMode) { @@ -214,4 +214,4 @@ namespace SixLabors.ImageSharp.PixelFormats } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index f557f348a1..b18b58510e 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The to the source vectors. /// The to the destination colors. /// The to apply during the conversion - internal virtual void FromVector4Destructive( + public virtual void FromVector4Destructive( Configuration configuration, Span sourceVectors, Span destPixels, @@ -56,8 +56,11 @@ namespace SixLabors.ImageSharp.PixelFormats /// A to configure internal operations /// The to the source vectors. /// The to the destination colors. - internal void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destPixels) => - this.FromVector4Destructive(configuration, sourceVectors, destPixels, PixelConversionModifiers.None); + public void FromVector4Destructive( + Configuration configuration, + Span sourceVectors, + Span destPixels) + => this.FromVector4Destructive(configuration, sourceVectors, destPixels, PixelConversionModifiers.None); /// /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. @@ -66,7 +69,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The to the source colors. /// The to the destination vectors. /// The to apply during the conversion - internal virtual void ToVector4( + public virtual void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, @@ -83,11 +86,11 @@ namespace SixLabors.ImageSharp.PixelFormats /// A to configure internal operations /// The to the source colors. /// The to the destination vectors. - internal virtual void ToVector4( + public void ToVector4( Configuration configuration, ReadOnlySpan sourcePixels, - Span destVectors) => - this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None); + Span destVectors) + => this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None); internal virtual void From( Configuration configuration, diff --git a/src/ImageSharp/Primitives/ColorMatrix.cs b/src/ImageSharp/Primitives/ColorMatrix.cs index 11886c9c2a..477d120faf 100644 --- a/src/ImageSharp/Primitives/ColorMatrix.cs +++ b/src/ImageSharp/Primitives/ColorMatrix.cs @@ -6,7 +6,7 @@ using System; using System.Globalization; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image. diff --git a/src/ImageSharp/Primitives/Complex64.cs b/src/ImageSharp/Primitives/Complex64.cs index 96883229c4..a5af3f2f71 100644 --- a/src/ImageSharp/Primitives/Complex64.cs +++ b/src/ImageSharp/Primitives/Complex64.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// Represents a complex number, where the real and imaginary parts are stored as values. diff --git a/src/ImageSharp/Primitives/ComplexVector4.cs b/src/ImageSharp/Primitives/ComplexVector4.cs index b90da65b2d..5287ab23ff 100644 --- a/src/ImageSharp/Primitives/ComplexVector4.cs +++ b/src/ImageSharp/Primitives/ComplexVector4.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// A vector with 4 values of type . diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index 170292e29e..4229e69e78 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -1,12 +1,11 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Diagnostics; using System.Runtime.CompilerServices; -using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// Represents a dense matrix with arbitrary elements. @@ -98,9 +97,9 @@ namespace SixLabors.ImageSharp.Primitives } /// - /// Gets a Span wrapping the Data. + /// Gets a span wrapping the . /// - internal Span Span => new Span(this.Data); + public Span Span => new Span(this.Data); /// /// Gets or sets the item at the specified position. @@ -222,4 +221,4 @@ namespace SixLabors.ImageSharp.Primitives /// public override int GetHashCode() => this.Data.GetHashCode(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Primitives/LongRational.cs b/src/ImageSharp/Primitives/LongRational.cs index b15aa4022f..d0f56917e1 100644 --- a/src/ImageSharp/Primitives/LongRational.cs +++ b/src/ImageSharp/Primitives/LongRational.cs @@ -5,7 +5,7 @@ using System; using System.Globalization; using System.Text; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// Represents a number that can be expressed as a fraction. diff --git a/src/ImageSharp/Primitives/Matrix3x2Extensions.cs b/src/ImageSharp/Primitives/Matrix3x2Extensions.cs new file mode 100644 index 0000000000..4ddbcc0173 --- /dev/null +++ b/src/ImageSharp/Primitives/Matrix3x2Extensions.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the struct. + /// + public static class Matrix3x2Extensions + { + /// + /// Creates a translation matrix from the given vector. + /// + /// The translation position. + /// A translation matrix. + public static Matrix3x2 CreateTranslation(PointF position) => Matrix3x2.CreateTranslation(position); + + /// + /// Creates a scale matrix that is offset by a given center point. + /// + /// Value to scale by on the X-axis. + /// Value to scale by on the Y-axis. + /// The center point. + /// A scaling matrix. + public static Matrix3x2 CreateScale(float xScale, float yScale, PointF centerPoint) => Matrix3x2.CreateScale(xScale, yScale, centerPoint); + + /// + /// Creates a scale matrix from the given vector scale. + /// + /// The scale to use. + /// A scaling matrix. + public static Matrix3x2 CreateScale(SizeF scales) => Matrix3x2.CreateScale(scales); + + /// + /// Creates a scale matrix from the given vector scale with an offset from the given center point. + /// + /// The scale to use. + /// The center offset. + /// A scaling matrix. + public static Matrix3x2 CreateScale(SizeF scales, PointF centerPoint) => Matrix3x2.CreateScale(scales, centerPoint); + + /// + /// Creates a scale matrix that scales uniformly with the given scale with an offset from the given center. + /// + /// The uniform scale to use. + /// The center offset. + /// A scaling matrix. + public static Matrix3x2 CreateScale(float scale, PointF centerPoint) => Matrix3x2.CreateScale(scale, centerPoint); + + /// + /// Creates a skew matrix from the given angles in degrees. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// A skew matrix. + public static Matrix3x2 CreateSkewDegrees(float degreesX, float degreesY) => Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); + + /// + /// Creates a skew matrix from the given angles in radians and a center point. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The center point. + /// A skew matrix. + public static Matrix3x2 CreateSkew(float radiansX, float radiansY, PointF centerPoint) => Matrix3x2.CreateSkew(radiansX, radiansY, centerPoint); + + /// + /// Creates a skew matrix from the given angles in degrees and a center point. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The center point. + /// A skew matrix. + public static Matrix3x2 CreateSkewDegrees(float degreesX, float degreesY, PointF centerPoint) => Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), centerPoint); + + /// + /// Creates a rotation matrix using the given rotation in degrees. + /// + /// The amount of rotation, in degrees. + /// A rotation matrix. + public static Matrix3x2 CreateRotationDegrees(float degrees) => Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees)); + + /// + /// Creates a rotation matrix using the given rotation in radians and a center point. + /// + /// The amount of rotation, in radians. + /// The center point. + /// A rotation matrix. + public static Matrix3x2 CreateRotation(float radians, PointF centerPoint) => Matrix3x2.CreateRotation(radians, centerPoint); + + /// + /// Creates a rotation matrix using the given rotation in degrees and a center point. + /// + /// The amount of rotation, in degrees. + /// The center point. + /// A rotation matrix. + public static Matrix3x2 CreateRotationDegrees(float degrees, PointF centerPoint) => Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees), centerPoint); + } +} diff --git a/src/ImageSharp/Primitives/Number.cs b/src/ImageSharp/Primitives/Number.cs new file mode 100644 index 0000000000..3d575e866b --- /dev/null +++ b/src/ImageSharp/Primitives/Number.cs @@ -0,0 +1,187 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents an integral number. + /// + [StructLayout(LayoutKind.Explicit)] + public struct Number : IEquatable, IComparable + { + [FieldOffset(0)] + private readonly int signedValue; + + [FieldOffset(0)] + private readonly uint unsignedValue; + + [FieldOffset(4)] + private readonly bool isSigned; + + /// + /// Initializes a new instance of the struct. + /// + /// The value of the number. + public Number(int value) + : this() + { + this.signedValue = value; + this.isSigned = true; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The value of the number. + public Number(uint value) + : this() + { + this.unsignedValue = value; + this.isSigned = false; + } + + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(int value) => new Number(value); + + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(uint value) => new Number(value); + + /// + /// Converts the specified to an instance of this type. + /// + /// The value. + public static implicit operator Number(ushort value) => new Number((uint)value); + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator int(Number number) + { + return number.isSigned + ? number.signedValue + : (int)number.unsignedValue.Clamp(0, int.MaxValue); + } + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator uint(Number number) + { + return number.isSigned + ? (uint)number.signedValue.Clamp(0, int.MaxValue) + : number.unsignedValue; + } + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator ushort(Number number) + { + return number.isSigned + ? (ushort)number.signedValue.Clamp(ushort.MinValue, ushort.MaxValue) + : (ushort)number.unsignedValue.Clamp(ushort.MinValue, ushort.MaxValue); + } + + /// + /// Determines whether the specified instances are considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator ==(Number left, Number right) => Equals(left, right); + + /// + /// Determines whether the specified instances are not considered equal. + /// + /// The first to compare. + /// The second to compare. + public static bool operator !=(Number left, Number right) => !Equals(left, right); + + /// + /// Determines whether the first is more than the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator >(Number left, Number right) => left.CompareTo(right) == 1; + + /// + /// Determines whether the first is less than the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator <(Number left, Number right) => left.CompareTo(right) == -1; + + /// + /// Determines whether the first is more than or equal to the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator >=(Number left, Number right) => left.CompareTo(right) >= 0; + + /// + /// Determines whether the first is less than or equal to the second . + /// + /// The first to compare. + /// The second to compare. + public static bool operator <=(Number left, Number right) => left.CompareTo(right) <= 0; + + /// + public int CompareTo(Number other) + { + return this.isSigned + ? this.signedValue.CompareTo(other.signedValue) + : this.unsignedValue.CompareTo(other.unsignedValue); + } + + /// + public override bool Equals(object obj) => obj is Number other && this.Equals(other); + + /// + public bool Equals(Number other) + { + if (this.isSigned != other.isSigned) + { + return false; + } + + return this.isSigned + ? this.signedValue.Equals(other.signedValue) + : this.unsignedValue.Equals(other.unsignedValue); + } + + /// + public override int GetHashCode() + { + return this.isSigned + ? this.signedValue.GetHashCode() + : this.unsignedValue.GetHashCode(); + } + + /// + public override string ToString() => this.ToString(CultureInfo.InvariantCulture); + + /// + /// Converts the numeric value of this instance to its equivalent string representation using the specified culture-specific format information. + /// + /// An object that supplies culture-specific formatting information. + /// The string representation of the value of this instance, which consists of a sequence of digits ranging from 0 to 9, without a sign or leading zeros. + public string ToString(IFormatProvider provider) + { + return this.isSigned + ? this.signedValue.ToString(provider) + : this.unsignedValue.ToString(provider); + } + } +} diff --git a/src/ImageSharp/Primitives/Point.cs b/src/ImageSharp/Primitives/Point.cs new file mode 100644 index 0000000000..96e73766b9 --- /dev/null +++ b/src/ImageSharp/Primitives/Point.cs @@ -0,0 +1,288 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents an ordered pair of integer x- and y-coordinates that defines a point in + /// a two-dimensional plane. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct Point : IEquatable + { + /// + /// Represents a that has X and Y values set to zero. + /// + public static readonly Point Empty = default; + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal and vertical position of the point. + public Point(int value) + : this() + { + this.X = LowInt16(value); + this.Y = HighInt16(value); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal position of the point. + /// The vertical position of the point. + public Point(int x, int y) + : this() + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The size. + public Point(Size size) + { + this.X = size.Width; + this.Y = size.Height; + } + + /// + /// Gets or sets the x-coordinate of this . + /// + public int X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public int Y { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PointF(Point point) => new PointF(point.X, point.Y); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Size(Point point) => new Size(point.X, point.Y); + + /// + /// Negates the given point by multiplying all values by -1. + /// + /// The source point. + /// The negated point. + public static Point operator -(Point value) => new Point(-value.X, -value.Y); + + /// + /// Translates a by a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point operator +(Point point, Size size) => Add(point, size); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point operator -(Point point, Size size) => Subtract(point, size); + + /// + /// Multiplies by a producing . + /// + /// Multiplier of type . + /// Multiplicand of type . + /// Product of type . + public static Point operator *(int left, Point right) => Multiply(right, left); + + /// + /// Multiplies by a producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type . + public static Point operator *(Point left, int right) => Multiply(left, right); + + /// + /// Divides by a producing . + /// + /// Dividend of type . + /// Divisor of type . + /// Result of type . + public static Point operator /(Point left, int right) + => new Point(left.X / right, left.Y / right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Point left, Point right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Point left, Point right) => !left.Equals(right); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height)); + + /// + /// Translates a by the negative of a given value. + /// + /// The point on the left hand of the operand. + /// The value on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Multiply(Point point, int value) => new Point(unchecked(point.X * value), unchecked(point.Y * value)); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height)); + + /// + /// Converts a to a by performing a ceiling operation on all the coordinates. + /// + /// The point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y))); + + /// + /// Converts a to a by performing a round operation on all the coordinates. + /// + /// The point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); + + /// + /// Converts a to a by performing a round operation on all the coordinates. + /// + /// The vector. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y))); + + /// + /// Converts a to a by performing a truncate operation on all the coordinates. + /// + /// The point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y)); + + /// + /// Transforms a point by a specified 3x2 matrix. + /// + /// The point to transform. + /// The transformation matrix used. + /// The transformed . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Transform(Point point, Matrix3x2 matrix) => Round(Vector2.Transform(new Vector2(point.X, point.Y), matrix)); + + /// + /// Deconstructs this point into two integers. + /// + /// The out value for X. + /// The out value for Y. + public void Deconstruct(out int x, out int y) + { + x = this.X; + y = this.Y; + } + + /// + /// Translates this by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(int dx, int dy) + { + unchecked + { + this.X += dx; + this.Y += dy; + } + } + + /// + /// Translates this by the specified amount. + /// + /// The used offset this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(Point point) => this.Offset(point.X, point.Y); + + /// + public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + + /// + public override string ToString() => $"Point [ X={this.X}, Y={this.Y} ]"; + + /// + public override bool Equals(object obj) => obj is Point other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Point other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + + private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); + + private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/PointF.cs b/src/ImageSharp/Primitives/PointF.cs new file mode 100644 index 0000000000..e43ad4daf1 --- /dev/null +++ b/src/ImageSharp/Primitives/PointF.cs @@ -0,0 +1,293 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents an ordered pair of single precision floating point x- and y-coordinates that defines a point in + /// a two-dimensional plane. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct PointF : IEquatable + { + /// + /// Represents a that has X and Y values set to zero. + /// + public static readonly PointF Empty = default; + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal position of the point. + /// The vertical position of the point. + public PointF(float x, float y) + : this() + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The size. + public PointF(SizeF size) + { + this.X = size.Width; + this.Y = size.Height; + } + + /// + /// Gets or sets the x-coordinate of this . + /// + public float X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public float Y { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The vector. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PointF(Vector2 vector) => new PointF(vector.X, vector.Y); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector2(PointF point) => new Vector2(point.X, point.Y); + + /// + /// Creates a with the coordinates of the specified by truncating each of the coordinates. + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Point(PointF point) => Point.Truncate(point); + + /// + /// Negates the given point by multiplying all values by -1. + /// + /// The source point. + /// The negated point. + public static PointF operator -(PointF value) => new PointF(-value.X, -value.Y); + + /// + /// Translates a by a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator +(PointF point, SizeF size) => Add(point, size); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator -(PointF point, PointF size) => Subtract(point, size); + + /// + /// Translates a by a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator +(PointF point, PointF size) => Add(point, size); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator -(PointF point, SizeF size) => Subtract(point, size); + + /// + /// Multiplies by a producing . + /// + /// Multiplier of type . + /// Multiplicand of type . + /// Product of type . + public static PointF operator *(float left, PointF right) => Multiply(right, left); + + /// + /// Multiplies by a producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type . + public static PointF operator *(PointF left, float right) => Multiply(left, right); + + /// + /// Divides by a producing . + /// + /// Dividend of type . + /// Divisor of type . + /// Result of type . + public static PointF operator /(PointF left, float right) + => new PointF(left.X / right, left.Y / right); + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(PointF left, PointF right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(PointF left, PointF right) => !left.Equals(right); + + /// + /// Translates a by the given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Add(PointF point, SizeF size) => new PointF(point.X + size.Width, point.Y + size.Height); + + /// + /// Translates a by the given . + /// + /// The point on the left hand of the operand. + /// The point on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Add(PointF point, PointF pointb) => new PointF(point.X + pointb.X, point.Y + pointb.Y); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Subtract(PointF point, SizeF size) => new PointF(point.X - size.Width, point.Y - size.Height); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The point on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Subtract(PointF point, PointF pointb) => new PointF(point.X - pointb.X, point.Y - pointb.Y); + + /// + /// Translates a by the multiplying the X and Y by the given value. + /// + /// The point on the left hand of the operand. + /// The value on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Multiply(PointF point, float right) => new PointF(point.X * right, point.Y * right); + + /// + /// Transforms a point by a specified 3x2 matrix. + /// + /// The point to transform. + /// The transformation matrix used. + /// The transformed . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Transform(PointF point, Matrix3x2 matrix) => Vector2.Transform(point, matrix); + + /// + /// Deconstructs this point into two floats. + /// + /// The out value for X. + /// The out value for Y. + public void Deconstruct(out float x, out float y) + { + x = this.X; + y = this.Y; + } + + /// + /// Translates this by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(float dx, float dy) + { + this.X += dx; + this.Y += dy; + } + + /// + /// Translates this by the specified amount. + /// + /// The used offset this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(PointF point) => this.Offset(point.X, point.Y); + + /// + public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + + /// + public override string ToString() => $"PointF [ X={this.X}, Y={this.Y} ]"; + + /// + public override bool Equals(object obj) => obj is PointF && this.Equals((PointF)obj); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/Rational.cs b/src/ImageSharp/Primitives/Rational.cs index f9299bc178..212178a246 100644 --- a/src/ImageSharp/Primitives/Rational.cs +++ b/src/ImageSharp/Primitives/Rational.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// Represents a number that can be expressed as a fraction. diff --git a/src/ImageSharp/Primitives/Rectangle.cs b/src/ImageSharp/Primitives/Rectangle.cs new file mode 100644 index 0000000000..95b01fd9d6 --- /dev/null +++ b/src/ImageSharp/Primitives/Rectangle.cs @@ -0,0 +1,463 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Stores a set of four integers that represent the location and size of a rectangle. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct Rectangle : IEquatable + { + /// + /// Represents a that has X, Y, Width, and Height values set to zero. + /// + public static readonly Rectangle Empty = default; + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal position of the rectangle. + /// The vertical position of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + public Rectangle(int x, int y, int width, int height) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The which specifies the rectangles point in a two-dimensional plane. + /// + /// + /// The which specifies the rectangles height and width. + /// + public Rectangle(Point point, Size size) + { + this.X = point.X; + this.Y = point.Y; + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Gets or sets the x-coordinate of this . + /// + public int X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public int Y { get; set; } + + /// + /// Gets or sets the width of this . + /// + public int Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public int Height { get; set; } + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Point Location + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Point(this.X, this.Y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.X = value.X; + this.Y = value.Y; + } + } + + /// + /// Gets or sets the size of this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Size Size + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Size(this.Width, this.Height); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.Width = value.Width; + this.Height = value.Height; + } + } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Gets the y-coordinate of the top edge of this . + /// + public int Top => this.Y; + + /// + /// Gets the x-coordinate of the right edge of this . + /// + public int Right + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => unchecked(this.X + this.Width); + } + + /// + /// Gets the y-coordinate of the bottom edge of this . + /// + public int Bottom + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => unchecked(this.Y + this.Height); + } + + /// + /// Gets the x-coordinate of the left edge of this . + /// + public int Left => this.X; + + /// + /// Creates a with the coordinates of the specified . + /// + /// The rectangle. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator RectangleF(Rectangle rectangle) => new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The rectangle. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector4(Rectangle rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right); + + /// + /// Creates a new with the specified location and size. + /// The left coordinate of the rectangle. + /// The top coordinate of the rectangle. + /// The right coordinate of the rectangle. + /// The bottom coordinate of the rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + // ReSharper disable once InconsistentNaming + public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); + + /// + /// Returns the center point of the given . + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Center(Rectangle rectangle) => new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + + /// + /// Creates a rectangle that represents the intersection between and + /// . If there is no intersection, an empty rectangle is returned. + /// + /// The first rectangle. + /// The second rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Intersect(Rectangle a, Rectangle b) + { + int x1 = Math.Max(a.X, b.X); + int x2 = Math.Min(a.Right, b.Right); + int y1 = Math.Max(a.Y, b.Y); + int y2 = Math.Min(a.Bottom, b.Bottom); + + if (x2 >= x1 && y2 >= y1) + { + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + return Empty; + } + + /// + /// Creates a that is inflated by the specified amount. + /// + /// The rectangle. + /// The amount to inflate the width by. + /// The amount to inflate the height by. + /// A new . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Inflate(Rectangle rectangle, int x, int y) + { + Rectangle r = rectangle; + r.Inflate(x, y); + return r; + } + + /// + /// Converts a to a by performing a ceiling operation on all the coordinates. + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Ceiling(RectangleF rectangle) + { + unchecked + { + return new Rectangle( + (int)MathF.Ceiling(rectangle.X), + (int)MathF.Ceiling(rectangle.Y), + (int)MathF.Ceiling(rectangle.Width), + (int)MathF.Ceiling(rectangle.Height)); + } + } + + /// + /// Transforms a rectangle by the given matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// A transformed rectangle. + public static RectangleF Transform(Rectangle rectangle, Matrix3x2 matrix) + { + PointF bottomRight = Point.Transform(new Point(rectangle.Right, rectangle.Bottom), matrix); + PointF topLeft = Point.Transform(rectangle.Location, matrix); + return new RectangleF(topLeft, new SizeF(bottomRight - topLeft)); + } + + /// + /// Converts a to a by performing a truncate operation on all the coordinates. + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Truncate(RectangleF rectangle) + { + unchecked + { + return new Rectangle( + (int)rectangle.X, + (int)rectangle.Y, + (int)rectangle.Width, + (int)rectangle.Height); + } + } + + /// + /// Converts a to a by performing a round operation on all the coordinates. + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Round(RectangleF rectangle) + { + unchecked + { + return new Rectangle( + (int)MathF.Round(rectangle.X), + (int)MathF.Round(rectangle.Y), + (int)MathF.Round(rectangle.Width), + (int)MathF.Round(rectangle.Height)); + } + } + + /// + /// Creates a rectangle that represents the union between and . + /// + /// The first rectangle. + /// The second rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Union(Rectangle a, Rectangle b) + { + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.Right, b.Right); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Bottom, b.Bottom); + + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + /// + /// Deconstructs this rectangle into four integers. + /// + /// The out value for X. + /// The out value for Y. + /// The out value for the width. + /// The out value for the height. + public void Deconstruct(out int x, out int y, out int width, out int height) + { + x = this.X; + y = this.Y; + width = this.Width; + height = this.Height; + } + + /// + /// Creates a Rectangle that represents the intersection between this Rectangle and the . + /// + /// The rectangle. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Intersect(Rectangle rectangle) + { + Rectangle result = Intersect(rectangle, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; + } + + /// + /// Inflates this by the specified amount. + /// + /// The width. + /// The height. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(int width, int height) + { + unchecked + { + this.X -= width; + this.Y -= height; + + this.Width += 2 * width; + this.Height += 2 * height; + } + } + + /// + /// Inflates this by the specified amount. + /// + /// The size. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(Size size) => this.Inflate(size.Width, size.Height); + + /// + /// Determines if the specfied point is contained within the rectangular region defined by + /// this . + /// + /// The x-coordinate of the given point. + /// The y-coordinate of the given point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(int x, int y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this . + /// + /// The point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(Point point) => this.Contains(point.X, point.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained + /// within the rectangular region represented by this . + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(Rectangle rectangle) => + (this.X <= rectangle.X) && (rectangle.Right <= this.Right) && + (this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom); + + /// + /// Determines if the specfied intersects the rectangular region defined by + /// this . + /// + /// The other Rectange. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IntersectsWith(Rectangle rectangle) => + (rectangle.X < this.Right) && (this.X < rectangle.Right) && + (rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(Point point) => this.Offset(point.X, point.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(int dx, int dy) + { + unchecked + { + this.X += dx; + this.Y += dy; + } + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(this.X, this.Y, this.Width, this.Height); + } + + /// + public override string ToString() + { + return $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; + } + + /// + public override bool Equals(object obj) => obj is Rectangle other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rectangle other) => + this.X.Equals(other.X) && + this.Y.Equals(other.Y) && + this.Width.Equals(other.Width) && + this.Height.Equals(other.Height); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/RectangleF.cs b/src/ImageSharp/Primitives/RectangleF.cs new file mode 100644 index 0000000000..354daa4463 --- /dev/null +++ b/src/ImageSharp/Primitives/RectangleF.cs @@ -0,0 +1,396 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Stores a set of four single precision floating points that represent the location and size of a rectangle. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct RectangleF : IEquatable + { + /// + /// Represents a that has X, Y, Width, and Height values set to zero. + /// + public static readonly RectangleF Empty = default; + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal position of the rectangle. + /// The vertical position of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + public RectangleF(float x, float y, float width, float height) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The which specifies the rectangles point in a two-dimensional plane. + /// + /// + /// The which specifies the rectangles height and width. + /// + public RectangleF(PointF point, SizeF size) + { + this.X = point.X; + this.Y = point.Y; + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Gets or sets the x-coordinate of this . + /// + public float X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public float Y { get; set; } + + /// + /// Gets or sets the width of this . + /// + public float Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public float Height { get; set; } + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public PointF Location + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new PointF(this.X, this.Y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.X = value.X; + this.Y = value.Y; + } + } + + /// + /// Gets or sets the size of this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public SizeF Size + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new SizeF(this.Width, this.Height); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.Width = value.Width; + this.Height = value.Height; + } + } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => (this.Width <= 0) || (this.Height <= 0); + + /// + /// Gets the y-coordinate of the top edge of this . + /// + public float Top => this.Y; + + /// + /// Gets the x-coordinate of the right edge of this . + /// + public float Right + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.X + this.Width; + } + + /// + /// Gets the y-coordinate of the bottom edge of this . + /// + public float Bottom + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.Y + this.Height; + } + + /// + /// Gets the x-coordinate of the left edge of this . + /// + public float Left => this.X; + + /// + /// Creates a with the coordinates of the specified by truncating each coordinate. + /// + /// The rectangle. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Rectangle(RectangleF rectangle) => Rectangle.Truncate(rectangle); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(RectangleF left, RectangleF right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(RectangleF left, RectangleF right) => !left.Equals(right); + + /// + /// Creates a new with the specified location and size. + /// The left coordinate of the rectangle. + /// The top coordinate of the rectangle. + /// The right coordinate of the rectangle. + /// The bottom coordinate of the rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + // ReSharper disable once InconsistentNaming + public static RectangleF FromLTRB(float left, float top, float right, float bottom) => new RectangleF(left, top, right - left, bottom - top); + + /// + /// Returns the center point of the given . + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Center(RectangleF rectangle) => new PointF(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + + /// + /// Creates a rectangle that represents the intersection between and + /// . If there is no intersection, an empty rectangle is returned. + /// + /// The first rectangle. + /// The second rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Intersect(RectangleF a, RectangleF b) + { + float x1 = MathF.Max(a.X, b.X); + float x2 = MathF.Min(a.Right, b.Right); + float y1 = MathF.Max(a.Y, b.Y); + float y2 = MathF.Min(a.Bottom, b.Bottom); + + if (x2 >= x1 && y2 >= y1) + { + return new RectangleF(x1, y1, x2 - x1, y2 - y1); + } + + return Empty; + } + + /// + /// Creates a that is inflated by the specified amount. + /// + /// The rectangle. + /// The amount to inflate the width by. + /// The amount to inflate the height by. + /// A new . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Inflate(RectangleF rectangle, float x, float y) + { + RectangleF r = rectangle; + r.Inflate(x, y); + return r; + } + + /// + /// Transforms a rectangle by the given matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// A transformed . + public static RectangleF Transform(RectangleF rectangle, Matrix3x2 matrix) + { + PointF bottomRight = PointF.Transform(new PointF(rectangle.Right, rectangle.Bottom), matrix); + PointF topLeft = PointF.Transform(rectangle.Location, matrix); + return new RectangleF(topLeft, new SizeF(bottomRight - topLeft)); + } + + /// + /// Creates a rectangle that represents the union between and . + /// + /// The first rectangle. + /// The second rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Union(RectangleF a, RectangleF b) + { + float x1 = MathF.Min(a.X, b.X); + float x2 = MathF.Max(a.Right, b.Right); + float y1 = MathF.Min(a.Y, b.Y); + float y2 = MathF.Max(a.Bottom, b.Bottom); + + return new RectangleF(x1, y1, x2 - x1, y2 - y1); + } + + /// + /// Deconstructs this rectangle into four floats. + /// + /// The out value for X. + /// The out value for Y. + /// The out value for the width. + /// The out value for the height. + public void Deconstruct(out float x, out float y, out float width, out float height) + { + x = this.X; + y = this.Y; + width = this.Width; + height = this.Height; + } + + /// + /// Creates a RectangleF that represents the intersection between this RectangleF and the . + /// + /// The rectangle. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Intersect(RectangleF rectangle) + { + RectangleF result = Intersect(rectangle, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; + } + + /// + /// Inflates this by the specified amount. + /// + /// The width. + /// The height. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(float width, float height) + { + this.X -= width; + this.Y -= height; + + this.Width += 2 * width; + this.Height += 2 * height; + } + + /// + /// Inflates this by the specified amount. + /// + /// The size. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(SizeF size) => this.Inflate(size.Width, size.Height); + + /// + /// Determines if the specfied point is contained within the rectangular region defined by + /// this . + /// + /// The x-coordinate of the given point. + /// The y-coordinate of the given point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(float x, float y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this . + /// + /// The point. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(PointF point) => this.Contains(point.X, point.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained + /// within the rectangular region represented by this . + /// + /// The rectangle. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(RectangleF rectangle) => + (this.X <= rectangle.X) && (rectangle.Right <= this.Right) && + (this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom); + + /// + /// Determines if the specfied intersects the rectangular region defined by + /// this . + /// + /// The other Rectange. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IntersectsWith(RectangleF rectangle) => + (rectangle.X < this.Right) && (this.X < rectangle.Right) && + (rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(PointF point) => this.Offset(point.X, point.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(float dx, float dy) + { + this.X += dx; + this.Y += dy; + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(this.X, this.Y, this.Width, this.Height); + } + + /// + public override string ToString() + { + return $"RectangleF [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; + } + + /// + public override bool Equals(object obj) => obj is RectangleF other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RectangleF other) => + this.X.Equals(other.X) && + this.Y.Equals(other.Y) && + this.Width.Equals(other.Width) && + this.Height.Equals(other.Height); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/SignedRational.cs b/src/ImageSharp/Primitives/SignedRational.cs index 395a24b14e..93a0ffe39f 100644 --- a/src/ImageSharp/Primitives/SignedRational.cs +++ b/src/ImageSharp/Primitives/SignedRational.cs @@ -4,7 +4,7 @@ using System; using System.Globalization; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// Represents a number that can be expressed as a fraction. diff --git a/src/ImageSharp/Primitives/Size.cs b/src/ImageSharp/Primitives/Size.cs new file mode 100644 index 0000000000..effd657a6c --- /dev/null +++ b/src/ImageSharp/Primitives/Size.cs @@ -0,0 +1,296 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Stores an ordered pair of integers, which specify a height and width. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct Size : IEquatable + { + /// + /// Represents a that has Width and Height values set to zero. + /// + public static readonly Size Empty = default; + + /// + /// Initializes a new instance of the struct. + /// + /// The width and height of the size. + public Size(int value) + : this() + { + this.Width = value; + this.Height = value; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The width of the size. + /// The height of the size. + public Size(int width, int height) + { + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The size. + public Size(Size size) + : this() + { + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The point. + public Size(Point point) + { + this.Width = point.X; + this.Height = point.Y; + } + + /// + /// Gets or sets the width of this . + /// + public int Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public int Height { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the dimensions of the specified . + /// + /// The point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SizeF(Size size) => new SizeF(size.Width, size.Height); + + /// + /// Converts the given into a . + /// + /// The size. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Point(Size size) => new Point(size.Width, size.Height); + + /// + /// Computes the sum of adding two sizes. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size operator +(Size left, Size right) => Add(left, right); + + /// + /// Computes the difference left by subtracting one size from another. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size operator -(Size left, Size right) => Subtract(left, right); + + /// + /// Multiplies a by an producing . + /// + /// Multiplier of type . + /// Multiplicand of type . + /// Product of type . + public static Size operator *(int left, Size right) => Multiply(right, left); + + /// + /// Multiplies by an producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type . + public static Size operator *(Size left, int right) => Multiply(left, right); + + /// + /// Divides by an producing . + /// + /// Dividend of type . + /// Divisor of type . + /// Result of type . + public static Size operator /(Size left, int right) => new Size(unchecked(left.Width / right), unchecked(left.Height / right)); + + /// + /// Multiplies by a producing . + /// + /// Multiplier of type . + /// Multiplicand of type . + /// Product of type . + public static SizeF operator *(float left, Size right) => Multiply(right, left); + + /// + /// Multiplies by a producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type . + public static SizeF operator *(Size left, float right) => Multiply(left, right); + + /// + /// Divides by a producing . + /// + /// Dividend of type . + /// Divisor of type . + /// Result of type . + public static SizeF operator /(Size left, float right) + => new SizeF(left.Width / right, left.Height / right); + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Size left, Size right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Size left, Size right) => !left.Equals(right); + + /// + /// Performs vector addition of two objects. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Add(Size left, Size right) => new Size(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height)); + + /// + /// Contracts a by another . + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Subtract(Size left, Size right) => new Size(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height)); + + /// + /// Converts a to a by performing a ceiling operation on all the dimensions. + /// + /// The size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Ceiling(SizeF size) => new Size(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height))); + + /// + /// Converts a to a by performing a round operation on all the dimensions. + /// + /// The size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Round(SizeF size) => new Size(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height))); + + /// + /// Transforms a size by the given matrix. + /// + /// The source size. + /// The transformation matrix. + /// A transformed size. + public static SizeF Transform(Size size, Matrix3x2 matrix) + { + var v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); + + return new SizeF(v.X, v.Y); + } + + /// + /// Converts a to a by performing a round operation on all the dimensions. + /// + /// The size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Truncate(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); + + /// + /// Deconstructs this size into two integers. + /// + /// The out value for the width. + /// The out value for the height. + public void Deconstruct(out int width, out int height) + { + width = this.Width; + height = this.Height; + } + + /// + public override int GetHashCode() => HashCode.Combine(this.Width, this.Height); + + /// + public override string ToString() => $"Size [ Width={this.Width}, Height={this.Height} ]"; + + /// + public override bool Equals(object obj) => obj is Size other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Size other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + + /// + /// Multiplies by an producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type . + private static Size Multiply(Size size, int multiplier) => + new Size(unchecked(size.Width * multiplier), unchecked(size.Height * multiplier)); + + /// + /// Multiplies by a producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type SizeF. + private static SizeF Multiply(Size size, float multiplier) => + new SizeF(size.Width * multiplier, size.Height * multiplier); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/SizeF.cs b/src/ImageSharp/Primitives/SizeF.cs new file mode 100644 index 0000000000..7d9bc58146 --- /dev/null +++ b/src/ImageSharp/Primitives/SizeF.cs @@ -0,0 +1,233 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Stores an ordered pair of single precision floating points, which specify a height and width. + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public struct SizeF : IEquatable + { + /// + /// Represents a that has Width and Height values set to zero. + /// + public static readonly SizeF Empty = default; + + /// + /// Initializes a new instance of the struct. + /// + /// The width of the size. + /// The height of the size. + public SizeF(float width, float height) + { + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The size. + public SizeF(SizeF size) + : this() + { + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The point. + public SizeF(PointF point) + { + this.Width = point.X; + this.Height = point.Y; + } + + /// + /// Gets or sets the width of this . + /// + public float Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public float Height { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector2(SizeF point) => new Vector2(point.Width, point.Height); + + /// + /// Creates a with the dimensions of the specified by truncating each of the dimensions. + /// + /// The size. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Size(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); + + /// + /// Converts the given into a . + /// + /// The size. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator PointF(SizeF size) => new PointF(size.Width, size.Height); + + /// + /// Computes the sum of adding two sizes. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF operator +(SizeF left, SizeF right) => Add(left, right); + + /// + /// Computes the difference left by subtracting one size from another. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF operator -(SizeF left, SizeF right) => Subtract(left, right); + + /// + /// Multiplies by a producing . + /// + /// Multiplier of type . + /// Multiplicand of type . + /// Product of type . + public static SizeF operator *(float left, SizeF right) => Multiply(right, left); + + /// + /// Multiplies by a producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type . + public static SizeF operator *(SizeF left, float right) => Multiply(left, right); + + /// + /// Divides by a producing . + /// + /// Dividend of type . + /// Divisor of type . + /// Result of type . + public static SizeF operator /(SizeF left, float right) + => new SizeF(left.Width / right, left.Height / right); + + /// + /// Compares two objects for equality. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SizeF left, SizeF right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SizeF left, SizeF right) => !left.Equals(right); + + /// + /// Performs vector addition of two objects. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF Add(SizeF left, SizeF right) => new SizeF(left.Width + right.Width, left.Height + right.Height); + + /// + /// Contracts a by another . + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF Subtract(SizeF left, SizeF right) => new SizeF(left.Width - right.Width, left.Height - right.Height); + + /// + /// Transforms a size by the given matrix. + /// + /// The source size. + /// The transformation matrix. + /// A transformed size. + public static SizeF Transform(SizeF size, Matrix3x2 matrix) + { + var v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); + + return new SizeF(v.X, v.Y); + } + + /// + /// Deconstructs this size into two floats. + /// + /// The out value for the width. + /// The out value for the height. + public void Deconstruct(out float width, out float height) + { + width = this.Width; + height = this.Height; + } + + /// + public override int GetHashCode() => HashCode.Combine(this.Width, this.Height); + + /// + public override string ToString() => $"SizeF [ Width={this.Width}, Height={this.Height} ]"; + + /// + public override bool Equals(object obj) => obj is SizeF && this.Equals((SizeF)obj); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + + /// + /// Multiplies by a producing . + /// + /// Multiplicand of type . + /// Multiplier of type . + /// Product of type SizeF. + private static SizeF Multiply(SizeF size, float multiplier) => + new SizeF(size.Width * multiplier, size.Height * multiplier); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/ValueSize.cs b/src/ImageSharp/Primitives/ValueSize.cs index 577e9187a6..be2ccb7251 100644 --- a/src/ImageSharp/Primitives/ValueSize.cs +++ b/src/ImageSharp/Primitives/ValueSize.cs @@ -2,9 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Primitives +namespace SixLabors.ImageSharp { /// /// Represents a value in relation to a value on the image. diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index c3d01241c9..90e00924ad 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Numerics; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/BokehBlurExecutionMode.cs b/src/ImageSharp/Processing/BokehBlurExecutionMode.cs deleted file mode 100644 index bc44dca03c..0000000000 --- a/src/ImageSharp/Processing/BokehBlurExecutionMode.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Processing.Processors.Convolution; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// An that indicates execution options for the . - /// - public enum BokehBlurExecutionMode - { - /// - /// Indicates that the maximum performance should be prioritized over memory usage. - /// - PreferMaximumPerformance, - - /// - /// Indicates that the memory usage should be prioritized over raw performance. - /// - PreferLowMemoryUsage - } -} diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index 328ccdf941..a4a3f9b3d4 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -1,11 +1,8 @@ // 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.Processors; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { @@ -23,10 +20,12 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// The image. - /// The mutate. - public DefaultImageProcessorContext(Image source, bool mutate) + /// The configuration which allows altering default behaviour or extending the library. + /// The source image. + /// Whether to mutate the image. + public DefaultImageProcessorContext(Configuration configuration, Image source, bool mutate) { + this.Configuration = configuration; this.mutate = mutate; this.source = source; @@ -38,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing } /// - public MemoryAllocator MemoryAllocator => this.source.GetConfiguration().MemoryAllocator; + public Configuration Configuration { get; } /// public Image GetResultImage() @@ -71,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing // interim clone if the first processor in the pipeline is a cloning processor. if (processor is ICloningImageProcessor cloningImageProcessor) { - using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.source, rectangle)) + using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.Configuration, this.source, rectangle)) { this.destination = pixelProcessor.CloneAndExecute(); return this; @@ -83,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing } // Standard processing pipeline. - using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.destination, rectangle)) + using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.Configuration, this.destination, rectangle)) { specificProcessor.Execute(); } diff --git a/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDiffuseExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs rename to src/ImageSharp/Processing/Extensions/Binarization/BinaryDiffuseExtensions.cs index 760102aacf..66337f669e 100644 --- a/src/ImageSharp/Processing/Extensions/BinaryDiffuseExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDiffuseExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs rename to src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs index e8ce252a2d..afd4a49418 100644 --- a/src/ImageSharp/Processing/Extensions/BinaryDitherExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Binarization/BinaryDitherExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs rename to src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs index 35aa681e33..d4fe9b562e 100644 --- a/src/ImageSharp/Processing/Extensions/BinaryThresholdExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Binarization/BinaryThresholdExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Binarization; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs deleted file mode 100644 index ef20f940af..0000000000 --- a/src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Adds bokeh blurring extensions to the type. - /// - public static class BokehBlurExtensions - { - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) - => source.ApplyProcessor(new BokehBlurProcessor()); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// The execution mode to use when applying the processor. - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, BokehBlurExecutionMode executionMode) - => source.ApplyProcessor(new BokehBlurProcessor(executionMode)); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// The 'radius' value representing the size of the area to sample. - /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. - /// The gamma highlight factor to use to emphasize bright spots in the source image - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) - => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// The 'radius' value representing the size of the area to sample. - /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. - /// The gamma highlight factor to use to emphasize bright spots in the source image - /// The execution mode to use when applying the processor. - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode) - => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode)); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) - => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The execution mode to use when applying the processor. - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle, BokehBlurExecutionMode executionMode) - => source.ApplyProcessor(new BokehBlurProcessor(executionMode), rectangle); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// The 'radius' value representing the size of the area to sample. - /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. - /// The gamma highlight factor to use to emphasize bright spots in the source image - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) - => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); - - /// - /// Applies a bokeh blur to the image. - /// - /// The image this method extends. - /// The 'radius' value representing the size of the area to sample. - /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. - /// The gamma highlight factor to use to emphasize bright spots in the source image - /// The execution mode to use when applying the processor. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The to allow chaining of operations. - public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode, Rectangle rectangle) - => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode), rectangle); - } -} diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs new file mode 100644 index 0000000000..7e0b3df390 --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/Convolution/BokehBlurExtensions.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors.Convolution; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Adds bokeh blurring extensions to the type. + /// + public static class BokehBlurExtensions + { + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new BokehBlurProcessor()); + + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); + + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new BokehBlurProcessor(), rectangle); + + /// + /// Applies a bokeh blur to the image. + /// + /// The image this method extends. + /// The 'radius' value representing the size of the area to sample. + /// The 'components' value representing the number of kernels to use to approximate the bokeh effect. + /// The gamma highlight factor to use to emphasize bright spots in the source image + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) + => source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); + } +} diff --git a/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs index 42dfd425cc..4534e474a9 100644 --- a/src/ImageSharp/Processing/Extensions/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs index 837b26910d..53b2d40b0d 100644 --- a/src/ImageSharp/Processing/Extensions/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/DetectEdgesExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs index 858e3213b1..9c40d94ed7 100644 --- a/src/ImageSharp/Processing/Extensions/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs rename to src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs index 79f4a0cc30..007fffb1a2 100644 --- a/src/ImageSharp/Processing/Extensions/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/Dithering/DiffuseExtensions.cs similarity index 97% rename from src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs rename to src/ImageSharp/Processing/Extensions/Dithering/DiffuseExtensions.cs index f9a1bdde0e..92d312fdf6 100644 --- a/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Dithering/DiffuseExtensions.cs @@ -1,12 +1,11 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Processing.Dithering +namespace SixLabors.ImageSharp.Processing { /// /// Defines extension methods to apply diffusion to an @@ -95,4 +94,4 @@ namespace SixLabors.ImageSharp.Processing.Dithering Rectangle rectangle) => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/DitherExtensions.cs b/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/DitherExtensions.cs rename to src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs index f83a9e9e81..f58b025f3a 100644 --- a/src/ImageSharp/Processing/Extensions/DitherExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs similarity index 91% rename from src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs rename to src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs index 981cf1bef4..4717c09eaf 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { @@ -22,14 +21,17 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image image, - float opacity) => - source.ApplyProcessor( + float opacity) + { + var options = new GraphicsOptions(); + return source.ApplyProcessor( new DrawImageProcessor( - image, - Point.Empty, - GraphicsOptions.Default.ColorBlendingMode, - GraphicsOptions.Default.AlphaCompositionMode, - opacity)); + image, + Point.Empty, + options.ColorBlendingMode, + options.AlphaCompositionMode, + opacity)); + } /// /// Draws the given image together with the current one by blending their pixels. @@ -49,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing image, Point.Empty, colorBlending, - GraphicsOptions.Default.AlphaCompositionMode, + new GraphicsOptions().AlphaCompositionMode, opacity)); /// @@ -100,14 +102,17 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, Image image, Point location, - float opacity) => - source.ApplyProcessor( + float opacity) + { + var options = new GraphicsOptions(); + return source.ApplyProcessor( new DrawImageProcessor( - image, - location, - GraphicsOptions.Default.ColorBlendingMode, - GraphicsOptions.Default.AlphaCompositionMode, - opacity)); + image, + location, + options.ColorBlendingMode, + options.AlphaCompositionMode, + opacity)); + } /// /// Draws the given image together with the current one by blending their pixels. @@ -129,7 +134,7 @@ namespace SixLabors.ImageSharp.Processing image, location, colorBlending, - GraphicsOptions.Default.AlphaCompositionMode, + new GraphicsOptions().AlphaCompositionMode, opacity)); /// @@ -172,4 +177,4 @@ namespace SixLabors.ImageSharp.Processing options.AlphaCompositionMode, options.BlendPercentage)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs rename to src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs index 1aa98c8c1b..5216172819 100644 --- a/src/ImageSharp/Processing/Extensions/OilPaintExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Effects/OilPaintExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Effects; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/Effects/PixelShaderExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/PixelShaderExtensions.cs new file mode 100644 index 0000000000..00fd542672 --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/Effects/PixelShaderExtensions.cs @@ -0,0 +1,102 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Effects; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Defines extension methods that allow the application of user defined pixel shaders to an . + /// + public static class PixelShaderExtensions + { + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PixelShader pixelShader) + => source.ApplyProcessor(new PixelShaderProcessor(pixelShader)); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// The to apply during the pixel conversions. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PixelShader pixelShader, PixelConversionModifiers modifiers) + => source.ApplyProcessor(new PixelShaderProcessor(pixelShader, modifiers)); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PixelShader pixelShader, Rectangle rectangle) + => source.ApplyProcessor(new PixelShaderProcessor(pixelShader), rectangle); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to apply during the pixel conversions. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PixelShader pixelShader, Rectangle rectangle, PixelConversionModifiers modifiers) + => source.ApplyProcessor(new PixelShaderProcessor(pixelShader, modifiers), rectangle); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PositionAwarePixelShader pixelShader) + => source.ApplyProcessor(new PositionAwarePixelShaderProcessor(pixelShader)); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// The to apply during the pixel conversions. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PositionAwarePixelShader pixelShader, PixelConversionModifiers modifiers) + => source.ApplyProcessor(new PositionAwarePixelShaderProcessor(pixelShader, modifiers)); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PositionAwarePixelShader pixelShader, Rectangle rectangle) + => source.ApplyProcessor(new PositionAwarePixelShaderProcessor(pixelShader), rectangle); + + /// + /// Applies a user defined pixel shader to the image. + /// + /// The image this method extends. + /// The user defined pixel shader to use to modify images. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to apply during the pixel conversions. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PositionAwarePixelShader pixelShader, Rectangle rectangle, PixelConversionModifiers modifiers) + => source.ApplyProcessor(new PositionAwarePixelShaderProcessor(pixelShader, modifiers), rectangle); + } +} diff --git a/src/ImageSharp/Processing/Extensions/PixelateExtensions.cs b/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/PixelateExtensions.cs rename to src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs index bf40af91ad..f2a10532d0 100644 --- a/src/ImageSharp/Processing/Extensions/PixelateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Effects/PixelateExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Effects; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs index c148ccbcb9..788677fc80 100644 --- a/src/ImageSharp/Processing/Extensions/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/BlackWhiteExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs index 8e43f06c5a..7bc441297e 100644 --- a/src/ImageSharp/Processing/Extensions/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/BrightnessExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs index b8d503955e..e214c5a164 100644 --- a/src/ImageSharp/Processing/Extensions/ColorBlindnessExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/ColorBlindnessExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/ContrastExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/ContrastExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs index bdfd7c98a4..4a3e460b85 100644 --- a/src/ImageSharp/Processing/Extensions/ContrastExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/ContrastExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/FilterExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs similarity index 96% rename from src/ImageSharp/Processing/Extensions/FilterExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs index 662e3a6e16..088f618840 100644 --- a/src/ImageSharp/Processing/Extensions/FilterExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/FilterExtensions.cs @@ -1,9 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs index d87c40226c..4125de8321 100644 --- a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/GrayscaleExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/HueExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/HueExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs index 3955ea7f6e..ef1fa2a6ec 100644 --- a/src/ImageSharp/Processing/Extensions/HueExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/HueExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/InvertExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/InvertExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs index 16c7a89178..0642db849d 100644 --- a/src/ImageSharp/Processing/Extensions/InvertExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/InvertExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs index 6c9b279835..eadbde7bc0 100644 --- a/src/ImageSharp/Processing/Extensions/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/KodachromeExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs new file mode 100644 index 0000000000..d68cb6aacb --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/Filters/LightnessExtensions.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors.Filters; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Defines extensions that allow the alteration of the lightness component of an + /// using Mutate/Clone. + /// + public static class LightnessExtensions + { + /// + /// Alters the lightness 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 lighter results. + /// + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The to allow chaining of operations. + public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new LightnessProcessor(amount)); + + /// + /// Alters the lightness 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 lighter results. + /// + /// 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 to allow chaining of operations. + public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new LightnessProcessor(amount), rectangle); + } +} diff --git a/src/ImageSharp/Processing/Extensions/LomographExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/LomographExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs index c2b6ac0804..84b11c5e71 100644 --- a/src/ImageSharp/Processing/Extensions/LomographExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/OpacityExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs index 9c67113ecf..2cf6085f34 100644 --- a/src/ImageSharp/Processing/Extensions/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/OpacityExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs index 6b6d43d5b8..94ced7108d 100644 --- a/src/ImageSharp/Processing/Extensions/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/SaturateExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs index a94a9a407d..f68c424bdc 100644 --- a/src/ImageSharp/Processing/Extensions/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/SaturateExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/SepiaExtensions.cs rename to src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs index df32307f47..629ba03e77 100644 --- a/src/ImageSharp/Processing/Extensions/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Filters/SepiaExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs b/src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/HistogramEqualizationExtensions.cs rename to src/ImageSharp/Processing/Extensions/Normalization/HistogramEqualizationExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs similarity index 88% rename from src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs rename to src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs index dd1cc1ed24..d068ba10b5 100644 --- a/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { @@ -19,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// The color to set as the background. /// The to allow chaining of operations. public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) => - BackgroundColor(source, GraphicsOptions.Default, color); + BackgroundColor(source, new GraphicsOptions(), color); /// /// Replaces the background color of image with the given one. @@ -34,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, Color color, Rectangle rectangle) => - BackgroundColor(source, GraphicsOptions.Default, color, rectangle); + BackgroundColor(source, new GraphicsOptions(), color, rectangle); /// /// Replaces the background color of image with the given one. @@ -47,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, GraphicsOptions options, Color color) => - source.ApplyProcessor(new BackgroundColorProcessor(color, options)); + source.ApplyProcessor(new BackgroundColorProcessor(options, color)); /// /// Replaces the background color of image with the given one. @@ -64,6 +63,6 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options, Color color, Rectangle rectangle) => - source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); + source.ApplyProcessor(new BackgroundColorProcessor(options, color), rectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/GlowExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs similarity index 93% rename from src/ImageSharp/Processing/Extensions/GlowExtensions.cs rename to src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs index 39734882b0..d5114e30ab 100644 --- a/src/ImageSharp/Processing/Extensions/GlowExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs @@ -1,9 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { @@ -19,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source) => - Glow(source, GraphicsOptions.Default); + Glow(source, new GraphicsOptions()); /// /// Applies a radial glow effect to an image. @@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color) { - return Glow(source, GraphicsOptions.Default, color); + return Glow(source, new GraphicsOptions(), color); } /// @@ -39,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// The the radius. /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) => - Glow(source, GraphicsOptions.Default, radius); + Glow(source, new GraphicsOptions(), radius); /// /// Applies a radial glow effect to an image. @@ -50,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) => - source.Glow(GraphicsOptions.Default, rectangle); + source.Glow(new GraphicsOptions(), rectangle); /// /// Applies a radial glow effect to an image. @@ -67,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing Color color, float radius, Rectangle rectangle) => - source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); + source.Glow(new GraphicsOptions(), color, ValueSize.Absolute(radius), rectangle); /// /// Applies a radial glow effect to an image. @@ -155,7 +153,7 @@ namespace SixLabors.ImageSharp.Processing Color color, ValueSize radius, Rectangle rectangle) => - source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); + source.ApplyProcessor(new GlowProcessor(options, color, radius), rectangle); /// /// Applies a radial glow effect to an image. @@ -170,6 +168,6 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options, Color color, ValueSize radius) => - source.ApplyProcessor(new GlowProcessor(color, radius, options)); + source.ApplyProcessor(new GlowProcessor(options, color, radius)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs similarity index 92% rename from src/ImageSharp/Processing/Extensions/VignetteExtensions.cs rename to src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs index 74a59d3e13..799b30e01e 100644 --- a/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs @@ -1,9 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { @@ -19,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source) => - Vignette(source, GraphicsOptions.Default); + Vignette(source, new GraphicsOptions()); /// /// Applies a radial vignette effect to an image. @@ -28,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// The color to set as the vignette. /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) => - Vignette(source, GraphicsOptions.Default, color); + Vignette(source, new GraphicsOptions(), color); /// /// Applies a radial vignette effect to an image. @@ -41,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, float radiusX, float radiusY) => - Vignette(source, GraphicsOptions.Default, radiusX, radiusY); + Vignette(source, new GraphicsOptions(), radiusX, radiusY); /// /// Applies a radial vignette effect to an image. @@ -52,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) => - Vignette(source, GraphicsOptions.Default, rectangle); + Vignette(source, new GraphicsOptions(), rectangle); /// /// Applies a radial vignette effect to an image. @@ -71,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing float radiusX, float radiusY, Rectangle rectangle) => - source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); + source.Vignette(new GraphicsOptions(), color, radiusX, radiusY, rectangle); /// /// Applies a radial vignette effect to an image. @@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.Processing ValueSize radiusX, ValueSize radiusY, Rectangle rectangle) => - source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); + source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY), rectangle); private static IImageProcessingContext VignetteInternal( this IImageProcessingContext source, @@ -174,6 +172,6 @@ namespace SixLabors.ImageSharp.Processing Color color, ValueSize radiusX, ValueSize radiusY) => - source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); + source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index 40b1c439e6..36966c2966 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs @@ -20,12 +20,22 @@ namespace SixLabors.ImageSharp.Processing /// The image to mutate. /// The operation to perform on the source. public static void Mutate(this Image source, Action operation) + => Mutate(source, source.GetConfiguration(), operation); + + /// + /// Mutates the source image by applying the image operation to it. + /// + /// The image to mutate. + /// The configuration which allows altering default behaviour or extending the library. + /// The operation to perform on the source. + public static void Mutate(this Image source, Configuration configuration, Action operation) { + Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - source.AcceptVisitor(new ProcessingVisitor(operation, true)); + source.AcceptVisitor(new ProcessingVisitor(configuration, operation, true)); } /// @@ -36,14 +46,25 @@ namespace SixLabors.ImageSharp.Processing /// The operation to perform on the source. public static void Mutate(this Image source, Action operation) where TPixel : struct, IPixel + => Mutate(source, source.GetConfiguration(), operation); + + /// + /// Mutates the source image by applying the image operation to it. + /// + /// The pixel format. + /// The image to mutate. + /// The configuration which allows altering default behaviour or extending the library. + /// The operation to perform on the source. + public static void Mutate(this Image source, Configuration configuration, Action operation) + where TPixel : struct, IPixel { + Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); IInternalImageProcessingContext operationsRunner - = source.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(source, true); + = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, true); operation(operationsRunner); } @@ -56,14 +77,24 @@ namespace SixLabors.ImageSharp.Processing /// The operations to perform on the source. public static void Mutate(this Image source, params IImageProcessor[] operations) where TPixel : struct, IPixel + => Mutate(source, source.GetConfiguration(), operations); + + /// + /// Mutates the source image by applying the operations to it. + /// + /// The pixel format. + /// The image to mutate. + /// The configuration which allows altering default behaviour or extending the library. + /// The operations to perform on the source. + public static void Mutate(this Image source, Configuration configuration, params IImageProcessor[] operations) + where TPixel : struct, IPixel { Guard.NotNull(source, nameof(source)); Guard.NotNull(operations, nameof(operations)); source.EnsureNotDisposed(); IInternalImageProcessingContext operationsRunner - = source.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(source, true); + = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, true); operationsRunner.ApplyProcessors(operations); } @@ -75,12 +106,23 @@ namespace SixLabors.ImageSharp.Processing /// The operation to perform on the clone. /// The new . public static Image Clone(this Image source, Action operation) + => Clone(source, source.GetConfiguration(), operation); + + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operation. + /// + /// The image to clone. + /// The configuration which allows altering default behaviour or extending the library. + /// The operation to perform on the clone. + /// The new . + public static Image Clone(this Image source, Configuration configuration, Action operation) { + Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - var visitor = new ProcessingVisitor(operation, false); + var visitor = new ProcessingVisitor(configuration, operation, false); source.AcceptVisitor(visitor); return visitor.ResultImage; } @@ -94,14 +136,26 @@ namespace SixLabors.ImageSharp.Processing /// The new public static Image Clone(this Image source, Action operation) where TPixel : struct, IPixel + => Clone(source, source.GetConfiguration(), operation); + + /// + /// 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 configuration which allows altering default behaviour or extending the library. + /// The operation to perform on the clone. + /// The new + public static Image Clone(this Image source, Configuration configuration, Action operation) + where TPixel : struct, IPixel { + Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); IInternalImageProcessingContext operationsRunner - = source.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(source, false); + = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, false); operation(operationsRunner); return operationsRunner.GetResultImage(); @@ -116,14 +170,26 @@ namespace SixLabors.ImageSharp.Processing /// The new public static Image Clone(this Image source, params IImageProcessor[] operations) where TPixel : struct, IPixel + => Clone(source, source.GetConfiguration(), operations); + + /// + /// 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 configuration which allows altering default behaviour or extending the library. + /// The operations to perform on the clone. + /// The new + public static Image Clone(this Image source, Configuration configuration, params IImageProcessor[] operations) + where TPixel : struct, IPixel { + Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); Guard.NotNull(operations, nameof(operations)); source.EnsureNotDisposed(); IInternalImageProcessingContext operationsRunner - = source.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(source, false); + = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, false); operationsRunner.ApplyProcessors(operations); return operationsRunner.GetResultImage(); @@ -149,12 +215,15 @@ namespace SixLabors.ImageSharp.Processing private class ProcessingVisitor : IImageVisitor { + private readonly Configuration configuration; + private readonly Action operation; private readonly bool mutate; - public ProcessingVisitor(Action operation, bool mutate) + public ProcessingVisitor(Configuration configuration, Action operation, bool mutate) { + this.configuration = configuration; this.operation = operation; this.mutate = mutate; } @@ -165,8 +234,7 @@ namespace SixLabors.ImageSharp.Processing where TPixel : struct, IPixel { IInternalImageProcessingContext operationsRunner = - image.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + this.configuration.ImageOperationsProvider.CreateImageProcessingContext(this.configuration, image, this.mutate); this.operation(operationsRunner); this.ResultImage = operationsRunner.GetResultImage(); diff --git a/src/ImageSharp/Processing/Extensions/QuantizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/QuantizeExtensions.cs rename to src/ImageSharp/Processing/Extensions/Quantization/QuantizeExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/AutoOrientExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/AutoOrientExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/CropExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/CropExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs index 7ec85169e2..5fc8125ea8 100644 --- a/src/ImageSharp/Processing/Extensions/CropExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/CropExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/EntropyCropExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/EntropyCropExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/EntropyCropExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/FlipExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/FlipExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/FlipExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/PadExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs similarity index 98% rename from src/ImageSharp/Processing/Extensions/PadExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs index 270380084f..33a6fc36d6 100644 --- a/src/ImageSharp/Processing/Extensions/PadExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing { /// diff --git a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/ResizeExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs index f494ed9094..882b177214 100644 --- a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/ResizeExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/Extensions/RotateExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/RotateExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/RotateExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/RotateFlipExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/RotateFlipExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/SkewExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs similarity index 100% rename from src/ImageSharp/Processing/Extensions/SkewExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/SkewExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs similarity index 99% rename from src/ImageSharp/Processing/Extensions/TransformExtensions.cs rename to src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs index 7fffb71d20..6305649557 100644 --- a/src/ImageSharp/Processing/Extensions/TransformExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs @@ -4,7 +4,6 @@ using System.Numerics; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/IImageProcessingContext.cs b/src/ImageSharp/Processing/IImageProcessingContext.cs index 509b1313d9..8b57a289d4 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { @@ -13,10 +11,9 @@ namespace SixLabors.ImageSharp.Processing public interface IImageProcessingContext { /// - /// Gets a reference to the used to allocate buffers - /// for this context. + /// Gets the configuration which allows altering default behaviour or extending the library. /// - MemoryAllocator MemoryAllocator { get; } + Configuration Configuration { get; } /// /// Gets the image dimensions at the current point in the processing pipeline. @@ -39,4 +36,4 @@ namespace SixLabors.ImageSharp.Processing /// The current operations class to allow chaining of operations. IImageProcessingContext ApplyProcessor(IImageProcessor processor); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/IImageProcessingContextFactory.cs b/src/ImageSharp/Processing/IImageProcessingContextFactory.cs index 948e70b445..e3051ccdd9 100644 --- a/src/ImageSharp/Processing/IImageProcessingContextFactory.cs +++ b/src/ImageSharp/Processing/IImageProcessingContextFactory.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -14,10 +14,11 @@ namespace SixLabors.ImageSharp.Processing /// Called during mutate operations to generate the image operations provider. /// /// The pixel format + /// The configuration which allows altering default behaviour or extending the library. /// The source image. /// A flag to determine whether image operations are allowed to mutate the source image. /// A new - IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) + IInternalImageProcessingContext CreateImageProcessingContext(Configuration configuration, Image source, bool mutate) where TPixel : struct, IPixel; } @@ -27,10 +28,10 @@ namespace SixLabors.ImageSharp.Processing internal class DefaultImageOperationsProviderFactory : IImageProcessingContextFactory { /// - public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) + public IInternalImageProcessingContext CreateImageProcessingContext(Configuration configuration, Image source, bool mutate) where TPixel : struct, IPixel { - return new DefaultImageProcessorContext(source, mutate); + return new DefaultImageProcessorContext(configuration, source, mutate); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/IInternalImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IInternalImageProcessingContext{TPixel}.cs index 55303b1ef9..9d023cca84 100644 --- a/src/ImageSharp/Processing/IInternalImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IInternalImageProcessingContext{TPixel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -13,10 +13,10 @@ namespace SixLabors.ImageSharp.Processing where TPixel : struct, IPixel { /// - /// Returns the result image to return by + /// Returns the result image to return by /// (and other overloads). /// /// The current image or a new image depending on whether it is requested to mutate the source image. Image GetResultImage(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index 1f36e2593a..268281e4fe 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Primitives; // Many of these matrices are translated from Chromium project where // SkScalar[] is memory-mapped to a row-major matrix. @@ -432,6 +431,32 @@ namespace SixLabors.ImageSharp.Processing return m; } + /// + /// Create a lightness 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 lighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The + public static ColorMatrix CreateLightnessFilter(float amount) + { + Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); + amount--; + + return new ColorMatrix + { + M11 = 1F, + M22 = 1F, + M33 = 1F, + M44 = 1F, + M51 = amount, + M52 = amount, + M53 = amount + }; + } + /// /// Create a sepia filter matrix using the given amount. /// The formula used matches the svg specification. diff --git a/src/ImageSharp/Processing/PixelShader.cs b/src/ImageSharp/Processing/PixelShader.cs new file mode 100644 index 0000000000..0245931fb5 --- /dev/null +++ b/src/ImageSharp/Processing/PixelShader.cs @@ -0,0 +1,15 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// A representing a user defined pixel shader. + /// + /// The target row of pixels to process. + /// The , , , and fields map the RGBA channels respectively. + public delegate void PixelShader(Span span); +} diff --git a/src/ImageSharp/Processing/PositionAwarePixelShader.cs b/src/ImageSharp/Processing/PositionAwarePixelShader.cs new file mode 100644 index 0000000000..c87d3ada6b --- /dev/null +++ b/src/ImageSharp/Processing/PositionAwarePixelShader.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// A representing a user defined pixel shader. + /// + /// The target row of pixels to process. + /// The initial horizontal and vertical offset for the input pixels to process. + /// The , , , and fields map the RGBA channels respectively. + public delegate void PositionAwarePixelShader(Span span, Point offset); +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs index 80164793b2..2878539795 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -70,8 +69,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public Color LowerColor { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - => new BinaryErrorDiffusionProcessor(this, source, sourceRectangle); + => new BinaryErrorDiffusionProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs index 7e3458ae3e..262e9d0242 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs @@ -6,7 +6,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// Performs binary threshold filtering against an image using error diffusion. /// /// The pixel format. - internal class BinaryErrorDiffusionProcessor : ImageProcessor + internal sealed class BinaryErrorDiffusionProcessor : ImageProcessor where TPixel : struct, IPixel { private readonly BinaryErrorDiffusionProcessor definition; @@ -22,11 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public BinaryErrorDiffusionProcessor(BinaryErrorDiffusionProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public BinaryErrorDiffusionProcessor(Configuration configuration, BinaryErrorDiffusionProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization IErrorDiffuser diffuser = this.definition.Diffuser; byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + bool isAlphaOnly = typeof(TPixel) == typeof(A8); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } TPixel transformedPixel = luminance >= threshold ? upperColor : lowerColor; - diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY); } } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs index 0a426c893a..1626bbe80e 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs @@ -5,7 +5,6 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -52,8 +51,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public Color LowerColor { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - => new BinaryOrderedDitherProcessor(this, source, sourceRectangle); + => new BinaryOrderedDitherProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs index e0aae0279f..66b92d1ce3 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs @@ -6,7 +6,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -22,11 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public BinaryOrderedDitherProcessor(BinaryOrderedDitherProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public BinaryOrderedDitherProcessor(Configuration configuration, BinaryOrderedDitherProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization TPixel upperColor = this.definition.UpperColor.ToPixel(); TPixel lowerColor = this.definition.LowerColor.ToPixel(); - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + bool isAlphaOnly = typeof(TPixel) == typeof(A8); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index a33c464694..7bfb024461 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -50,8 +49,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public Color LowerColor { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - => new BinaryThresholdProcessor(this, source, sourceRectangle); + => new BinaryThresholdProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 7234955edb..380ce64d24 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -4,9 +4,8 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -22,11 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public BinaryThresholdProcessor(BinaryThresholdProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public BinaryThresholdProcessor(Configuration configuration, BinaryThresholdProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization int startX = interest.X; int endX = interest.Right; - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + bool isAlphaOnly = typeof(TPixel) == typeof(A8); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs index 5e9ca2e542..92c84a9456 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -12,11 +11,11 @@ namespace SixLabors.ImageSharp.Processing.Processors public abstract class CloningImageProcessor : ICloningImageProcessor { /// - public abstract ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + public abstract ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; /// - IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - => this.CreatePixelSpecificCloningProcessor(source, sourceRectangle); + IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => this.CreatePixelSpecificCloningProcessor(configuration, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 42d2f0e1df..c539861f9b 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -2,11 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -22,13 +18,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The source for the current processor instance. /// The source area to process for the current processor instance. - protected CloningImageProcessor(Image source, Rectangle sourceRectangle) + protected CloningImageProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { + this.Configuration = configuration; this.Source = source; this.SourceRectangle = sourceRectangle; - this.Configuration = this.Source.GetConfiguration(); } /// @@ -54,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Image clone = this.CreateTarget(); this.CheckFrameCount(this.Source, clone); - Configuration configuration = this.Source.GetConfiguration(); + Configuration configuration = this.Configuration; this.BeforeImageApply(clone); for (int i = 0; i < this.Source.Frames.Count; i++) @@ -173,16 +170,19 @@ namespace SixLabors.ImageSharp.Processing.Processors Image source = this.Source; Size targetSize = this.GetTargetSize(); - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( - x => new ImageFrame( - source.GetConfiguration(), + // We will always be creating the clone even for mutate because we may need to resize the canvas. + var targetFrames = new ImageFrame[source.Frames.Count]; + for (int i = 0; i < targetFrames.Length; i++) + { + targetFrames[i] = new ImageFrame( + this.Configuration, targetSize.Width, targetSize.Height, - x.Metadata.DeepClone())); + source.Frames[i].Metadata.DeepClone()); + } - // Use the overload to prevent an extra frame being added - return new Image(this.Configuration, source.Metadata.DeepClone(), frames); + // Use the overload to prevent an extra frame being added. + return new Image(this.Configuration, source.Metadata.DeepClone(), targetFrames); } private void CheckFrameCount(Image a, Image b) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index b7e102deb5..6bb02f1d14 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -26,27 +25,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public const float DefaultGamma = 3F; - /// - /// The default execution mode used by the parameterless constructor. - /// - public const BokehBlurExecutionMode DefaultExecutionMode = BokehBlurExecutionMode.PreferLowMemoryUsage; - /// /// Initializes a new instance of the class. /// public BokehBlurProcessor() - : this(DefaultRadius, DefaultComponents, DefaultGamma, DefaultExecutionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The execution mode to use when applying the processor. - /// - public BokehBlurProcessor(BokehBlurExecutionMode executionMode) - : this(DefaultRadius, DefaultComponents, DefaultGamma, executionMode) + : this(DefaultRadius, DefaultComponents, DefaultGamma) { } @@ -63,33 +46,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The gamma highlight factor to use to further process the image. /// public BokehBlurProcessor(int radius, int components, float gamma) - : this(radius, components, gamma, DefaultExecutionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The 'radius' value representing the size of the area to sample. - /// - /// - /// The number of components to use to approximate the original 2D bokeh blur convolution kernel. - /// - /// - /// The gamma highlight factor to use to further process the image. - /// - /// - /// The execution mode to use when applying the processor. - /// - public BokehBlurProcessor(int radius, int components, float gamma, BokehBlurExecutionMode executionMode) { Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma)); this.Radius = radius; this.Components = components; this.Gamma = gamma; - this.ExecutionMode = executionMode; } /// @@ -107,16 +69,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public float Gamma { get; } - /// - /// Gets the execution mode to use when applying the effect. - /// - public BokehBlurExecutionMode ExecutionMode { get; } - /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new BokehBlurProcessor(this, source, sourceRectangle); - } + => new BokehBlurProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 9339c8fe43..316579da70 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -8,13 +8,10 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -36,11 +33,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// private readonly float gamma; - /// - /// The execution mode to use when applying the effect - /// - private readonly BokehBlurExecutionMode executionMode; - /// /// The maximum size of the kernel in either direction /// @@ -74,17 +66,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public BokehBlurProcessor(BokehBlurProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public BokehBlurProcessor(Configuration configuration, BokehBlurProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.radius = definition.Radius; this.kernelSize = (this.radius * 2) + 1; this.componentsCount = definition.Components; this.gamma = definition.Gamma; - this.executionMode = definition.ExecutionMode; // Reuse the initialized values from the cache, if possible var parameters = new BokehBlurParameters(this.radius, this.componentsCount); @@ -280,30 +272,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution this.ApplyGammaExposure(source.PixelBuffer, this.SourceRectangle, this.Configuration); // Create a 0-filled buffer to use to store the result of the component convolutions - using (Buffer2D processing = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) + using (Buffer2D processingBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) { - if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage) - { - // Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode - using (Buffer2D buffer = this.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius)) - using (Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height)) - using (Buffer2D secondPassBuffer = buffer.Slice(0, source.Height)) - { - this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassBuffer, secondPassBuffer); - } - } - else - { - // Performance priority: allocate two independent buffers and execute both convolutions in parallel mode - using (Buffer2D firstPassValues = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) - using (Buffer2D secondPassBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) - { - this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues, secondPassBuffer); - } - } + // Perform the 1D convolutions on all the kernel components and accumulate the results + this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processingBuffer); // Apply the inverse gamma exposure pass, and write the final pixel data - this.ApplyInverseGammaExposure(source.PixelBuffer, processing, this.SourceRectangle, this.Configuration); + this.ApplyInverseGammaExposure(source.PixelBuffer, processingBuffer, this.SourceRectangle, this.Configuration); } } @@ -314,29 +289,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The structure that specifies the portion of the image object to draw. /// The configuration. /// The buffer with the raw pixel data to use to aggregate the results of each convolution. - /// The complex buffer to use for the first 1D convolution pass for each kernel. - /// The complex buffer to use for the second 1D convolution pass for each kernel. private void OnFrameApplyCore( ImageFrame source, Rectangle sourceRectangle, Configuration configuration, - Buffer2D processingBuffer, - Buffer2D firstPassBuffer, - Buffer2D secondPassBuffer) + Buffer2D processingBuffer) { - // Perform two 1D convolutions for each component in the current instance - ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); - for (int i = 0; i < this.kernels.Length; i++) + using (Buffer2D firstPassBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { - // Compute the resulting complex buffer for the current component - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - Complex64[] kernel = Unsafe.Add(ref baseRef, i); - this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration); - this.ApplyConvolution(secondPassBuffer, firstPassBuffer, interest, kernel, configuration); - - // Add the results of the convolution with the current kernel - Vector4 parameters = this.kernelParameters[i]; - this.SumProcessingPartials(processingBuffer, secondPassBuffer, sourceRectangle, configuration, parameters.Z, parameters.W); + // Perform two 1D convolutions for each component in the current instance + ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan()); + ref Vector4 paramsRef = ref MemoryMarshal.GetReference(this.kernelParameters.AsSpan()); + for (int i = 0; i < this.kernels.Length; i++) + { + // Compute the resulting complex buffer for the current component + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + Complex64[] kernel = Unsafe.Add(ref baseRef, i); + Vector4 parameters = Unsafe.Add(ref paramsRef, i); + + // Compute the two 1D convolutions and accumulate the partial results on the target buffer + this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration); + this.ApplyConvolution(processingBuffer, firstPassBuffer, interest, kernel, configuration, parameters.Z, parameters.W); + } } } @@ -389,19 +363,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies the process to the specified portion of the specified buffer at the specified location /// and with the specified size. /// - /// The target values to use to store the results. + /// The target values to use to store the results. /// The source complex values. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// + /// The structure that specifies the portion of the image object to draw. /// The 1D kernel. /// The + /// The weight factor for the real component of the complex pixel values. + /// The weight factor for the imaginary component of the complex pixel values. private void ApplyConvolution( - Buffer2D targetValues, + Buffer2D targetValues, Buffer2D sourceValues, Rectangle sourceRectangle, Complex64[] kernel, - Configuration configuration) + Configuration configuration, + float z, + float w) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -413,12 +389,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; - if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage) - { - configuration = configuration.Clone(); - configuration.MaxDegreeOfParallelism = 1; - } - ParallelHelper.IterateRows( workingRectangle, configuration, @@ -426,11 +396,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { for (int y = rows.Min; y < rows.Max; y++) { - Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); + Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); for (int x = 0; x < width; x++) { - Buffer2DUtils.Convolve4(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX); + Buffer2DUtils.Convolve4AndAccumulatePartials(kernel, sourceValues, targetRowSpan, y, x, startY, maxY, startX, maxX, z, w); } } }); @@ -536,53 +506,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } }); } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The target instance to use to store the results. - /// The source complex pixels. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The - /// The weight factor for the real component of the complex pixel values. - /// The weight factor for the imaginary component of the complex pixel values. - private void SumProcessingPartials( - Buffer2D targetValues, - Buffer2D sourceValues, - Rectangle sourceRectangle, - Configuration configuration, - float z, - float w) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); - int width = workingRectangle.Width; - - ParallelHelper.IterateRows( - workingRectangle, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = targetValues.GetRowSpan(y).Slice(startX); - Span sourceRowSpan = sourceValues.GetRowSpan(y).Slice(startX); - ref Vector4 baseTargetRef = ref MemoryMarshal.GetReference(targetRowSpan); - ref ComplexVector4 baseSourceRef = ref MemoryMarshal.GetReference(sourceRowSpan); - - for (int x = 0; x < width; x++) - { - Unsafe.Add(ref baseTargetRef, x) += Unsafe.Add(ref baseSourceRef, x).WeightedSum(z, w); - } - } - }); - } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index a5368c4639..7ca4b6c6f3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Defines a box blur processor of a given Radius. + /// Defines a box blur processor of a given radius. /// public sealed class BoxBlurProcessor : IImageProcessor { @@ -41,10 +40,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public int Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new BoxBlurProcessor(this, source, sourceRectangle); - } + => new BoxBlurProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs index 77110e642d..095c91bac0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -14,18 +12,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution internal class BoxBlurProcessor : ImageProcessor where TPixel : struct, IPixel { - private readonly BoxBlurProcessor definition; - /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public BoxBlurProcessor(BoxBlurProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public BoxBlurProcessor(Configuration configuration, BoxBlurProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { - this.definition = definition; int kernelSize = (definition.Radius * 2) + 1; this.KernelX = CreateBoxKernel(kernelSize); this.KernelY = this.KernelX.Transpose(); @@ -44,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + using (var processor = new Convolution2PassProcessor(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) { processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index b38d87cd4f..c2b85a4ab8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -5,11 +5,9 @@ using System; using System.Numerics; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -23,18 +21,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether the convolution filter is applied to alpha as well as the color channels. /// The source for the current processor instance. /// The source area to process for the current processor instance. public Convolution2DProcessor( + Configuration configuration, in DenseMatrix kernelX, in DenseMatrix kernelY, bool preserveAlpha, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + : base(configuration, source, sourceRectangle) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index a523f7c227..32bdf6bc5c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -4,11 +4,10 @@ using System; using System.Numerics; using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -22,18 +21,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether the convolution filter is applied to alpha as well as the color channels. /// The source for the current processor instance. /// The source area to process for the current processor instance. public Convolution2PassProcessor( + Configuration configuration, in DenseMatrix kernelX, in DenseMatrix kernelY, bool preserveAlpha, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + : base(configuration, source, sourceRectangle) { this.KernelX = kernelX; this.KernelY = kernelY; diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs index 661ab523db..34b085fc6e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -3,8 +3,6 @@ using System; -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { internal static class ConvolutionProcessorHelpers diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 5bdec738d7..285bcab279 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -4,11 +4,10 @@ using System; using System.Numerics; using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -22,16 +21,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The 2d gradient operator. /// Whether the convolution filter is applied to alpha as well as the color channels. /// The source for the current processor instance. /// The source area to process for the current processor instance. public ConvolutionProcessor( + Configuration configuration, in DenseMatrix kernelXY, bool preserveAlpha, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + : base(configuration, source, sourceRectangle) { this.KernelXY = kernelXY; this.PreserveAlpha = preserveAlpha; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 8358abe7df..31c4fad79f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -2,9 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -18,18 +16,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether to convert the image to grayscale before performing edge detection. /// The source for the current processor instance. /// The source area to process for the current processor instance. internal EdgeDetector2DProcessor( + Configuration configuration, in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + : base(configuration, source, sourceRectangle) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); } base.BeforeImageApply(); @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using (var processor = new Convolution2DProcessor(this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle)) + using (var processor = new Convolution2DProcessor(this.Configuration, this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle)) { processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index dc9974c616..c1897bed8d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -6,12 +6,10 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -25,12 +23,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// Gets the kernels to use. /// Whether to convert the image to grayscale before performing edge detection. /// The source for the current processor instance. /// The source area to process for the current processor instance. - internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + internal EdgeDetectorCompassProcessor(Configuration configuration, CompassKernels kernels, bool grayscale, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.Grayscale = grayscale; this.Kernels = kernels; @@ -45,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); } base.BeforeImageApply(); @@ -70,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // we need a clean copy for each pass to start from using (ImageFrame cleanCopy = source.Clone()) { - using (var processor = new ConvolutionProcessor(kernels[0], true, this.Source, this.SourceRectangle)) + using (var processor = new ConvolutionProcessor(this.Configuration, kernels[0], true, this.Source, this.SourceRectangle)) { processor.Apply(source); } @@ -102,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { using (ImageFrame pass = cleanCopy.Clone()) { - using (var processor = new ConvolutionProcessor(kernels[i], true, this.Source, this.SourceRectangle)) + using (var processor = new ConvolutionProcessor(this.Configuration, kernels[i], true, this.Source, this.SourceRectangle)) { processor.Apply(pass); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index 24b95da696..eb7f07905b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -26,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool Grayscale { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public abstract IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index 5246dc3b72..ce19ba82d4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -2,9 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -18,12 +16,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The 2d gradient operator. /// Whether to convert the image to grayscale before performing edge detection. /// The source for the current processor instance. /// The target area to process for the current processor instance. - public EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public EdgeDetectorProcessor( + Configuration configuration, + in DenseMatrix kernelXY, + bool grayscale, + Image source, + Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.KernelXY = kernelXY; this.Grayscale = grayscale; @@ -41,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Configuration, this.Source, this.SourceRectangle); } base.BeforeImageApply(); @@ -50,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using (var processor = new ConvolutionProcessor(this.KernelXY, true, this.Source, this.SourceRectangle)) + using (var processor = new ConvolutionProcessor(this.Configuration, this.KernelXY, true, this.Source, this.SourceRectangle)) { processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index aabc8041d9..9f511a7541 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -71,10 +70,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public int Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new GaussianBlurProcessor(this, source, sourceRectangle); - } + => new GaussianBlurProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs index bbf36ea5e8..3c1f82caa8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -17,11 +15,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public GaussianBlurProcessor(GaussianBlurProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public GaussianBlurProcessor( + Configuration configuration, + GaussianBlurProcessor definition, + Image source, + Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { int kernelSize = (definition.Radius * 2) + 1; this.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma); @@ -41,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + using (var processor = new Convolution2PassProcessor(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) { processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 0262ec8e44..b1f47863d4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -11,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public sealed class GaussianSharpenProcessor : IImageProcessor { - /// + /// /// The default value for . /// public const float DefaultSigma = 3f; @@ -71,10 +70,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public int Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new GaussianSharpenProcessor(this, source, sourceRectangle); - } + => new GaussianSharpenProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs index dab55b2328..f4f27a42de 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -17,11 +15,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public GaussianSharpenProcessor(GaussianSharpenProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public GaussianSharpenProcessor( + Configuration configuration, + GaussianSharpenProcessor definition, + Image source, + Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { int kernelSize = (definition.Radius * 2) + 1; this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma); @@ -41,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) { - using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + using (var processor = new Convolution2PassProcessor(this.Configuration, this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) { processor.Apply(source); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs index 2026512617..90ed15aa33 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,14 +19,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetector2DProcessor( + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetector2DProcessor( + configuration, KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY, this.Grayscale, source, sourceRectangle); - } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs index f44de9105b..423fc65917 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { internal abstract class CompassKernels diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs index dd4d023025..50d5bfafe4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs index 882b87075b..58568ce409 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs index 19f2d1161b..8371212fe8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs index e7b7f965b9..f72e95ee8b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs index 381e028d49..cae9ecb5bf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs index f61220e1ec..8ffd624d21 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs index 699d669ec9..ba60bfdf64 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs index f0662c6672..ec583862f7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs index 113957c839..3dbd54a2c5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index bbbfc64d92..7207f95c4b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetectorCompassProcessor(new KirschKernels(), this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetectorCompassProcessor(configuration, new KirschKernels(), this.Grayscale, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index 64f99ebe61..b147a87cc8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetectorProcessor(LaplacianKernels.Laplacian3x3, this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetectorProcessor(configuration, LaplacianKernels.Laplacian3x3, this.Grayscale, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs index d1c909a941..663ebf0517 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetectorProcessor(LaplacianKernels.Laplacian5x5, this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetectorProcessor(configuration, LaplacianKernels.Laplacian5x5, this.Grayscale, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs index 0eecaefe16..8b0cfc6ff3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetectorProcessor(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetectorProcessor(configuration, LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs index 4338bcf6b9..5f03396bad 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Parameters/BokehBlurKernelData.cs @@ -3,8 +3,6 @@ using System.Numerics; -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs index 242e3f7b97..7fc54ff967 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetector2DProcessor(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetector2DProcessor( + configuration, + PrewittKernels.PrewittX, + PrewittKernels.PrewittY, + this.Grayscale, + source, + sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index 481a990ff9..74d5094f53 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,14 +19,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetector2DProcessor( + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetector2DProcessor( + configuration, RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, this.Grayscale, source, sourceRectangle); - } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index 324ed31545..18ac906140 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetectorCompassProcessor(new RobinsonKernels(), this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetectorCompassProcessor(configuration, new RobinsonKernels(), this.Grayscale, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index 6a4bf6afd5..24248204b6 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetector2DProcessor(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetector2DProcessor( + configuration, + ScharrKernels.ScharrX, + ScharrKernels.ScharrY, + this.Grayscale, + source, + sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 96ed3bcea5..1ab56d1203 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -21,9 +19,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new EdgeDetector2DProcessor( + configuration, + SobelKernels.SobelX, + SobelKernels.SobelY, + this.Grayscale, + source, + sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs index 0461d179ff..9cf10ce591 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,22 +9,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class AtkinsonDiffuser : ErrorDiffuser { + private const float Divisor = 8F; + /// /// The diffusion matrix /// private static readonly DenseMatrix AtkinsonMatrix = new float[,] { - { 0, 0, 1, 1 }, - { 1, 1, 1, 0 }, - { 0, 1, 0, 0 } + { 0, 0, 1 / Divisor, 1 / Divisor }, + { 1 / Divisor, 1 / Divisor, 1 / Divisor, 0 }, + { 0, 1 / Divisor, 0, 0 } }; /// /// Initializes a new instance of the class. /// public AtkinsonDiffuser() - : base(AtkinsonMatrix, 8) + : base(AtkinsonMatrix) { } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs index 23d4321e96..152704ec26 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,22 +9,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class BurksDiffuser : ErrorDiffuser { + private const float Divisor = 32F; + /// /// The diffusion matrix /// private static readonly DenseMatrix BurksMatrix = new float[,] { - { 0, 0, 0, 8, 4 }, - { 2, 4, 8, 4, 2 } + { 0, 0, 0, 8 / Divisor, 4 / Divisor }, + { 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor } }; /// /// Initializes a new instance of the class. /// public BurksDiffuser() - : base(BurksMatrix, 32) + : base(BurksMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs index 1c8156bf51..d6ccfb3694 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -6,7 +6,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -15,66 +14,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public abstract class ErrorDiffuser : IErrorDiffuser { - /// - /// The vector to perform division. - /// - private readonly Vector4 divisorVector; - - /// - /// The matrix width. - /// - private readonly int matrixHeight; - - /// - /// The matrix height. - /// - private readonly int matrixWidth; - - /// - /// The offset at which to start the dithering operation. - /// - private readonly int startingOffset; - - /// - /// The diffusion matrix. - /// + private readonly int offset; private readonly DenseMatrix matrix; /// /// Initializes a new instance of the class. /// /// The dithering matrix. - /// The divisor. - internal ErrorDiffuser(in DenseMatrix matrix, byte divisor) + internal ErrorDiffuser(in DenseMatrix matrix) { - Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); - - this.matrix = matrix; - this.matrixWidth = this.matrix.Columns; - this.matrixHeight = this.matrix.Rows; - this.divisorVector = new Vector4(divisor); + // Calculate the offset position of the pixel relative to + // the diffusion matrix. + this.offset = 0; - this.startingOffset = 0; - for (int i = 0; i < this.matrixWidth; i++) + for (int col = 0; col < matrix.Columns; col++) { - // Good to disable here as we are not comparing mathematical output. - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (matrix[0, i] != 0) + if (matrix[0, col] != 0) { - this.startingOffset = (byte)(i - 1); + this.offset = col - 1; break; } } + + this.matrix = matrix; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + [MethodImpl(InliningOptions.ShortMethod)] + public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int maxX, int maxY) where TPixel : struct, IPixel { image[x, y] = transformed; - // Equal? Break out as there's nothing to pass. + // Equal? Break out as there's no error to pass. if (source.Equals(transformed)) { return; @@ -82,45 +54,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering // Calculate the error Vector4 error = source.ToVector4() - transformed.ToVector4(); - this.DoDither(image, x, y, minX, minY, maxX, maxY, error); + this.DoDither(image, x, y, minX, maxX, maxY, error); } - [MethodImpl(MethodImplOptions.NoInlining)] - private void DoDither(ImageFrame image, int x, int y, int minX, int minY, int maxX, int maxY, Vector4 error) + [MethodImpl(InliningOptions.ShortMethod)] + private void DoDither(ImageFrame image, int x, int y, int minX, int maxX, int maxY, Vector4 error) where TPixel : struct, IPixel { + int offset = this.offset; + DenseMatrix matrix = this.matrix; + // Loop through and distribute the error amongst neighboring pixels. - for (int row = 0; row < this.matrixHeight; row++) + for (int row = 0, targetY = y; row < matrix.Rows && targetY < maxY; row++, targetY++) { - int matrixY = y + row; - if (matrixY > minY && matrixY < maxY) - { - Span rowSpan = image.GetPixelRowSpan(matrixY); + Span rowSpan = image.GetPixelRowSpan(targetY); - for (int col = 0; col < this.matrixWidth; col++) + for (int col = 0; col < matrix.Columns; col++) + { + int targetX = x + (col - offset); + if (targetX >= minX && targetX < maxX) { - int matrixX = x + (col - this.startingOffset); - - if (matrixX > minX && matrixX < maxX) + float coefficient = matrix[row, col]; + if (coefficient == 0) { - float coefficient = this.matrix[row, col]; - - // Good to disable here as we are not comparing mathematical output. - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (coefficient == 0) - { - continue; - } + continue; + } - ref TPixel pixel = ref rowSpan[matrixX]; - var offsetColor = pixel.ToVector4(); + ref TPixel pixel = ref rowSpan[targetX]; + var result = pixel.ToVector4(); - Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; - pixel.FromVector4(result); - } + result += error * coefficient; + pixel.FromVector4(result); } } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index 4e45130cc3..0598160655 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -58,9 +57,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public float Threshold { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { - return new ErrorDiffusionPaletteProcessor(this, source, sourceRectangle); + return new ErrorDiffusionPaletteProcessor(configuration, this, source, sourceRectangle); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs index 557a31c336..f0c8610ed7 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -13,17 +12,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// An that dithers an image using error diffusion. /// /// The pixel format. - internal class ErrorDiffusionPaletteProcessor : PaletteDitherProcessor + internal sealed class ErrorDiffusionPaletteProcessor : PaletteDitherProcessor where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public ErrorDiffusionPaletteProcessor(ErrorDiffusionPaletteProcessor definition, Image source, Rectangle sourceRectangle) - : base(definition, source, sourceRectangle) + public ErrorDiffusionPaletteProcessor(Configuration configuration, ErrorDiffusionPaletteProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition, source, sourceRectangle) { } @@ -33,8 +33,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering protected override void OnFrameApply(ImageFrame source) { byte threshold = (byte)MathF.Round(this.Definition.Threshold * 255F); - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; @@ -49,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + byte luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -72,14 +70,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First; - this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs index 78a28a693a..b3137337b4 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,22 +9,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class FloydSteinbergDiffuser : ErrorDiffuser { + private const float Divisor = 16F; + /// /// The diffusion matrix /// private static readonly DenseMatrix FloydSteinbergMatrix = new float[,] { - { 0, 0, 7 }, - { 3, 5, 1 } + { 0, 0, 7 / Divisor }, + { 3 / Divisor, 5 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public FloydSteinbergDiffuser() - : base(FloydSteinbergMatrix, 16) + : base(FloydSteinbergMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs index 5b30c0dc4d..8f4381d308 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -19,11 +19,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The column index. /// The row index. /// The minimum column value. - /// The minimum row value. /// The maximum column value. /// The maximum row value. /// The pixel format. - void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int maxX, int maxY) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs index 64c8610833..40cf792662 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,23 +9,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuser { + private const float Divisor = 48F; + /// /// The diffusion matrix /// private static readonly DenseMatrix JarvisJudiceNinkeMatrix = new float[,] { - { 0, 0, 0, 7, 5 }, - { 3, 5, 7, 5, 3 }, - { 1, 3, 5, 3, 1 } + { 0, 0, 0, 7 / Divisor, 5 / Divisor }, + { 3 / Divisor, 5 / Divisor, 7 / Divisor, 5 / Divisor, 3 / Divisor }, + { 1 / Divisor, 3 / Divisor, 5 / Divisor, 3 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public JarvisJudiceNinkeDiffuser() - : base(JarvisJudiceNinkeMatrix, 48) + : base(JarvisJudiceNinkeMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs index 174732f802..34eff4fe94 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs index 4b93c42590..f4835f4214 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherFactory.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index 87bb3e5171..e28c662f89 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -35,9 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public IOrderedDither Dither { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new OrderedDitherPaletteProcessor(this, source, sourceRectangle); - } + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new OrderedDitherPaletteProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs index 08eaec503d..29baa9750f 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -19,11 +18,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public OrderedDitherPaletteProcessor(OrderedDitherPaletteProcessor definition, Image source, Rectangle sourceRectangle) - : base(definition, source, sourceRectangle) + public OrderedDitherPaletteProcessor(Configuration configuration, OrderedDitherPaletteProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition, source, sourceRectangle) { } @@ -32,8 +32,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void OnFrameApply(ImageFrame source) { - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; @@ -48,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + byte luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -71,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs index 0de964b526..0a1552c115 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -28,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public ReadOnlyMemory Palette { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public abstract IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 9f817267fe..c9f09fc628 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -19,24 +19,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering where TPixel : struct, IPixel { private readonly Dictionary> cache = new Dictionary>(); - - private TPixel[] palette; - - /// - /// The vector representation of the image palette. - /// - private Vector4[] paletteVector; + private IMemoryOwner palette; + private IMemoryOwner paletteVector; + private bool palleteVectorMapped; + private bool isDisposed; /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - protected PaletteDitherProcessor(PaletteDitherProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + protected PaletteDitherProcessor(Configuration configuration, PaletteDitherProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.Definition = definition; + this.palette = this.Configuration.MemoryAllocator.Allocate(definition.Palette.Length); + this.paletteVector = this.Configuration.MemoryAllocator.Allocate(definition.Palette.Length); } protected PaletteDitherProcessor Definition { get; } @@ -44,28 +44,45 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void BeforeFrameApply(ImageFrame source) { - // Lazy init palette: - if (this.palette is null) + // Lazy init palettes: + if (!this.palleteVectorMapped) { ReadOnlySpan sourcePalette = this.Definition.Palette.Span; - this.palette = new TPixel[sourcePalette.Length]; - Color.ToPixel(this.Configuration, sourcePalette, this.palette); - } + Color.ToPixel(this.Configuration, sourcePalette, this.palette.Memory.Span); - // Lazy init paletteVector: - if (this.paletteVector is null) - { - this.paletteVector = new Vector4[this.palette.Length]; PixelOperations.Instance.ToVector4( this.Configuration, - (ReadOnlySpan)this.palette, - (Span)this.paletteVector, + this.palette.Memory.Span, + this.paletteVector.Memory.Span, PixelConversionModifiers.Scale); } + this.palleteVectorMapped = true; + base.BeforeFrameApply(source); } + /// + protected override void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.palette?.Dispose(); + this.paletteVector?.Dispose(); + } + + this.palette = null; + this.paletteVector = null; + + this.isDisposed = true; + base.Dispose(disposing); + } + /// /// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space. /// @@ -93,21 +110,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering TPixel closest = default; TPixel secondClosest = default; - for (int index = 0; index < this.paletteVector.Length; index++) + Span paletteSpan = this.palette.Memory.Span; + ref TPixel paletteSpanBase = ref MemoryMarshal.GetReference(paletteSpan); + Span paletteVectorSpan = this.paletteVector.Memory.Span; + ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); + + for (int index = 0; index < paletteVectorSpan.Length; index++) { - ref Vector4 candidate = ref this.paletteVector[index]; + ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, index); float distance = Vector4.DistanceSquared(vector, candidate); if (distance < leastDistance) { leastDistance = distance; secondClosest = closest; - closest = this.palette[index]; + closest = Unsafe.Add(ref paletteSpanBase, index); } else if (distance < secondLeastDistance) { secondLeastDistance = distance; - secondClosest = this.palette[index]; + secondClosest = Unsafe.Add(ref paletteSpanBase, index); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs index b489f8f28d..001df19af1 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,22 +9,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class Sierra2Diffuser : ErrorDiffuser { + private const float Divisor = 16F; + /// /// The diffusion matrix /// private static readonly DenseMatrix Sierra2Matrix = new float[,] { - { 0, 0, 0, 4, 3 }, - { 1, 2, 3, 2, 1 } + { 0, 0, 0, 4 / Divisor, 3 / Divisor }, + { 1 / Divisor, 2 / Divisor, 3 / Divisor, 2 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public Sierra2Diffuser() - : base(Sierra2Matrix, 16) + : base(Sierra2Matrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs index 04abc782a6..3e56c63b3b 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,23 +9,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class Sierra3Diffuser : ErrorDiffuser { + private const float Divisor = 32F; + /// /// The diffusion matrix /// private static readonly DenseMatrix Sierra3Matrix = new float[,] { - { 0, 0, 0, 5, 3 }, - { 2, 4, 5, 4, 2 }, - { 0, 2, 3, 2, 0 } + { 0, 0, 0, 5 / Divisor, 3 / Divisor }, + { 2 / Divisor, 4 / Divisor, 5 / Divisor, 4 / Divisor, 2 / Divisor }, + { 0, 2 / Divisor, 3 / Divisor, 2 / Divisor, 0 } }; /// /// Initializes a new instance of the class. /// public Sierra3Diffuser() - : base(Sierra3Matrix, 32) + : base(Sierra3Matrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs index 2ac69cf456..763695d661 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,22 +9,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class SierraLiteDiffuser : ErrorDiffuser { + private const float Divisor = 4F; + /// /// The diffusion matrix /// private static readonly DenseMatrix SierraLiteMatrix = new float[,] { - { 0, 0, 2 }, - { 1, 1, 0 } + { 0, 0, 2 / Divisor }, + { 1 / Divisor, 1 / Divisor, 0 } }; /// /// Initializes a new instance of the class. /// public SierraLiteDiffuser() - : base(SierraLiteMatrix, 4) + : base(SierraLiteMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs index b929a28d30..72ff30c114 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -10,24 +8,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class StevensonArceDiffuser : ErrorDiffuser { + private const float Divisor = 200F; + /// /// The diffusion matrix /// private static readonly DenseMatrix StevensonArceMatrix = new float[,] { - { 0, 0, 0, 0, 0, 32, 0 }, - { 12, 0, 26, 0, 30, 0, 16 }, - { 0, 12, 0, 26, 0, 12, 0 }, - { 5, 0, 12, 0, 12, 0, 5 } + { 0, 0, 0, 0, 0, 32 / Divisor, 0 }, + { 12 / Divisor, 0, 26 / Divisor, 0, 30 / Divisor, 0, 16 / Divisor }, + { 0, 12 / Divisor, 0, 26 / Divisor, 0, 12 / Divisor, 0 }, + { 5 / Divisor, 0, 12 / Divisor, 0, 12 / Divisor, 0, 5 / Divisor } }; /// /// Initializes a new instance of the class. /// public StevensonArceDiffuser() - : base(StevensonArceMatrix, 200) + : base(StevensonArceMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs index bb3aebc3f5..78e8fb4e49 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs @@ -1,8 +1,6 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Dithering { /// @@ -11,23 +9,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class StuckiDiffuser : ErrorDiffuser { + private const float Divisor = 42F; + /// /// The diffusion matrix /// private static readonly DenseMatrix StuckiMatrix = new float[,] { - { 0, 0, 0, 8, 4 }, - { 2, 4, 8, 4, 2 }, - { 1, 2, 4, 2, 1 } + { 0, 0, 0, 8 / Divisor, 4 / Divisor }, + { 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor }, + { 1 / Divisor, 2 / Divisor, 4 / Divisor, 2 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public StuckiDiffuser() - : base(StuckiMatrix, 42) + : base(StuckiMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt b/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt index ea412f6351..27dea8af17 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt +++ b/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt @@ -1,3 +1,6 @@ +Reference: +http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/error_diffusion.txt + List of error diffusion schemes. Quantization error of *current* pixel is added to the pixels diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs similarity index 88% rename from src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs rename to src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs index e217fd9a6c..032a0aab01 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { @@ -60,10 +59,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public float Opacity { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixelBg : struct, IPixel { - var visitor = new ProcessorFactoryVisitor(this, source, sourceRectangle); + var visitor = new ProcessorFactoryVisitor(configuration, this, source, sourceRectangle); this.Image.AcceptVisitor(visitor); return visitor.Result; } @@ -71,12 +70,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing private class ProcessorFactoryVisitor : IImageVisitor where TPixelBg : struct, IPixel { + private readonly Configuration configuration; private readonly DrawImageProcessor definition; private readonly Image source; private readonly Rectangle sourceRectangle; - public ProcessorFactoryVisitor(DrawImageProcessor definition, Image source, Rectangle sourceRectangle) + public ProcessorFactoryVisitor(Configuration configuration, DrawImageProcessor definition, Image source, Rectangle sourceRectangle) { + this.configuration = configuration; this.definition = definition; this.source = source; this.sourceRectangle = sourceRectangle; @@ -88,6 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing where TPixelFg : struct, IPixel { this.Result = new DrawImageProcessor( + this.configuration, image, this.source, this.sourceRectangle, diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs similarity index 94% rename from src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs rename to src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 76082136c7..a8b9093e5e 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -4,9 +4,8 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { @@ -22,6 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The foreground to blend with the currently processing image. /// The source for the current processor instance. /// The source area to process for the current processor instance. @@ -30,6 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The Alpha blending mode to use when drawing the image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor( + Configuration configuration, Image image, Image source, Rectangle sourceRectangle, @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) - : base(source, sourceRectangle) + : base(configuration, source, sourceRectangle) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 6f9e1869a3..a35e4d828b 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { @@ -40,10 +39,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects public int BrushSize { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new OilPaintingProcessor(this, source, sourceRectangle); - } + => new OilPaintingProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index ea7ba7409d..472c07aa7f 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -2,13 +2,14 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { @@ -25,11 +26,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public OilPaintingProcessor(OilPaintingProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public OilPaintingProcessor(Configuration configuration, OilPaintingProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } @@ -52,82 +54,113 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int radius = brushSize >> 1; int levels = this.definition.Levels; + int rowWidth = source.Width; + int rectangleWidth = this.SourceRectangle.Width; + + Configuration configuration = this.Configuration; + + using Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size()); + source.CopyTo(targetPixels); + + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + ParallelHelper.IterateRows( + workingRect, + this.Configuration, + (rows) => + { + /* Allocate the two temporary Vector4 buffers, one for the source row and one for the target row. + * The ParallelHelper.IterateRowsWithTempBuffers overload is not used in this case because + * the two allocated buffers have a length equal to the width of the source image, + * and not just equal to the width of the target rectangle to process. + * Furthermore, there are two buffers being allocated in this case, so using that overload would + * have still required the explicit allocation of the secondary buffer. + * Similarly, one temporary float buffer is also allocated from the pool, and that is used + * to create the target bins for all the color channels being processed. + * This buffer is only rented once outside of the main processing loop, and its contents + * are cleared for each loop iteration, to avoid the repeated allocation for each processed pixel. */ + using (IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth)) + using (IMemoryOwner targetRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth)) + using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4)) + { + Span sourceRowVector4Span = sourceRowBuffer.Memory.Span; + Span sourceRowAreaVector4Span = sourceRowVector4Span.Slice(startX, rectangleWidth); + + Span targetRowVector4Span = targetRowBuffer.Memory.Span; + Span targetRowAreaVector4Span = targetRowVector4Span.Slice(startX, rectangleWidth); + + ref float binsRef = ref bins.GetReference(); + ref int intensityBinRef = ref Unsafe.As(ref binsRef); + ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); + + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRowPixelSpan = source.GetPixelRowSpan(y); + Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(startX, rectangleWidth); - using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) - { - source.CopyTo(targetPixels); + PixelOperations.Instance.ToVector4(configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); - var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRows( - workingRect, - this.Configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) + for (int x = startX; x < endX; x++) { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) - { - int maxIntensity = 0; - int maxIndex = 0; + int maxIntensity = 0; + int maxIndex = 0; - var intensityBin = new int[levels]; - var redBin = new float[levels]; - var blueBin = new float[levels]; - var greenBin = new float[levels]; + // Clear the current shared buffer before processing each target pixel + bins.Memory.Span.Clear(); - for (int fy = 0; fy <= radius; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; - offsetY = offsetY.Clamp(0, maxY); + offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - var vector = sourceOffsetRow[offsetX].ToVector4(); + var vector = sourceOffsetRow[offsetX].ToVector4(); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; - int currentIntensity = (int)MathF.Round( - (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - intensityBin[currentIntensity]++; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; + Unsafe.Add(ref intensityBinRef, currentIntensity)++; + Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; - } + if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + { + maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIndex = currentIntensity; } + } - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float alpha = sourceRowVector4Span[x].W; - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4( - new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); - } + targetRowVector4Span[x] = new Vector4(red, green, blue, alpha); } } - }); - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } + Span targetRowAreaPixelSpan = targetPixels.GetRowSpan(y).Slice(startX, rectangleWidth); + + PixelOperations.Instance.FromVector4Destructive(configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); + } + } + }); + + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessor.cs new file mode 100644 index 0000000000..fef80dfc46 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessor.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a user defined pixel shader effect through a given delegate. + /// + public sealed class PixelShaderProcessor : IImageProcessor + { + /// + /// The default to apply during the pixel conversions. + /// + public const PixelConversionModifiers DefaultModifiers = PixelConversionModifiers.None; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The user defined pixel shader to use to modify images. + /// + public PixelShaderProcessor(PixelShader pixelShader) + { + this.PixelShader = pixelShader; + this.Modifiers = DefaultModifiers; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The user defined pixel shader to use to modify images. + /// + /// The to apply during the pixel conversions. + public PixelShaderProcessor(PixelShader pixelShader, PixelConversionModifiers modifiers) + { + this.PixelShader = pixelShader; + this.Modifiers = modifiers; + } + + /// + /// Gets the user defined pixel shader. + /// + public PixelShader PixelShader { get; } + + /// + /// Gets the to apply during the pixel conversions. + /// + public PixelConversionModifiers Modifiers { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel + => new PixelShaderProcessor(configuration, this, source, sourceRectangle); + } +} diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessorBase.cs b/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessorBase.cs new file mode 100644 index 0000000000..681f44651f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessorBase.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a user defined pixel shader effect through a given delegate. + /// + /// The pixel format. + internal abstract class PixelShaderProcessorBase : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// The to apply during the pixel conversions. + /// + private readonly PixelConversionModifiers modifiers; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The to apply during the pixel conversions. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected PixelShaderProcessorBase(Configuration configuration, PixelConversionModifiers modifiers, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) + { + this.modifiers = modifiers; + } + + /// + protected override void OnFrameApply(ImageFrame source) + { + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + int startX = interest.X; + Configuration configuration = this.Configuration; + PixelConversionModifiers modifiers = this.modifiers; + + ParallelHelper.IterateRowsWithTempBuffer( + interest, + this.Configuration, + (rows, vectorBuffer) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); + PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan, modifiers); + + // Run the user defined pixel shader on the current row of pixels + this.ApplyPixelShader(vectorSpan, new Point(startX, y)); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan, modifiers); + } + }); + } + + /// + /// Applies the current pixel shader effect on a target row of preprocessed pixels. + /// + /// The target row of pixels to process. + /// The initial horizontal and vertical offset for the input pixels to process. + protected abstract void ApplyPixelShader(Span span, Point offset); + } +} diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessor{TPixel}.cs new file mode 100644 index 0000000000..244cfe3a70 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PixelShaderProcessor{TPixel}.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a user defined pixel shader effect through a given delegate. + /// + /// The pixel format. + internal sealed class PixelShaderProcessor : PixelShaderProcessorBase + where TPixel : struct, IPixel + { + /// + /// The user defined pixel shader. + /// + private readonly PixelShader pixelShader; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public PixelShaderProcessor(Configuration configuration, PixelShaderProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition.Modifiers, source, sourceRectangle) + { + this.pixelShader = definition.PixelShader; + } + + /// + protected override void ApplyPixelShader(Span span, Point offset) => this.pixelShader(span); + } +} diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index 2d7cef8fff..a71f8424fc 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { @@ -30,10 +29,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects public int Size { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new PixelateProcessor(this, source, sourceRectangle); - } + => new PixelateProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs index 53acc351cf..df85afc5eb 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { @@ -24,11 +23,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The . /// The source for the current processor instance. /// The source area to process for the current processor instance. - public PixelateProcessor(PixelateProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public PixelateProcessor(Configuration configuration, PixelateProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } diff --git a/src/ImageSharp/Processing/Processors/Effects/PositionAwarePixelShaderProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PositionAwarePixelShaderProcessor.cs new file mode 100644 index 0000000000..7494f6ffc0 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PositionAwarePixelShaderProcessor.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a user defined pixel shader effect through a given delegate. + /// + public sealed class PositionAwarePixelShaderProcessor : IImageProcessor + { + /// + /// The default to apply during the pixel conversions. + /// + public const PixelConversionModifiers DefaultModifiers = PixelConversionModifiers.None; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The user defined pixel shader to use to modify images. + /// + public PositionAwarePixelShaderProcessor(PositionAwarePixelShader pixelShader) + { + this.PixelShader = pixelShader; + this.Modifiers = DefaultModifiers; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The user defined pixel shader to use to modify images. + /// + /// The to apply during the pixel conversions. + public PositionAwarePixelShaderProcessor(PositionAwarePixelShader pixelShader, PixelConversionModifiers modifiers) + { + this.PixelShader = pixelShader; + this.Modifiers = modifiers; + } + + /// + /// Gets the user defined pixel shader. + /// + public PositionAwarePixelShader PixelShader { get; } + + /// + /// Gets the to apply during the pixel conversions. + /// + public PixelConversionModifiers Modifiers { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel + => new PositionAwarePixelShaderProcessor(configuration, this, source, sourceRectangle); + } +} diff --git a/src/ImageSharp/Processing/Processors/Effects/PositionAwarePixelShaderProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PositionAwarePixelShaderProcessor{TPixel}.cs new file mode 100644 index 0000000000..a539b5105f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PositionAwarePixelShaderProcessor{TPixel}.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a user defined pixel shader effect through a given delegate. + /// + /// The pixel format. + internal sealed class PositionAwarePixelShaderProcessor : PixelShaderProcessorBase + where TPixel : struct, IPixel + { + /// + /// The user defined pixel shader. + /// + private readonly PositionAwarePixelShader pixelShader; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration which allows altering default behaviour or extending the library. + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public PositionAwarePixelShaderProcessor(Configuration configuration, PositionAwarePixelShaderProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition.Modifiers, source, sourceRectangle) + { + this.pixelShader = definition.PixelShader; + } + + /// + protected override void ApplyPixelShader(Span span, Point offset) => this.pixelShader(span, offset); + } +} diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 9cd4a9e460..1542c6836b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { @@ -24,10 +22,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters public ColorMatrix Matrix { get; } /// - public virtual IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public virtual IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new FilterProcessor(this, source, sourceRectangle); - } + => new FilterProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 5fa233d180..28a5837dea 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -4,10 +4,8 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { @@ -23,11 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The . /// The source for the current processor instance. /// The source area to process for the current processor instance. - public FilterProcessor(FilterProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public FilterProcessor(Configuration configuration, FilterProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } diff --git a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs new file mode 100644 index 0000000000..49be3b6a67 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + /// + /// Applies a lightness filter matrix using the given amount. + /// + public sealed class LightnessProcessor : FilterProcessor + { + /// + /// 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 lighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + public LightnessProcessor(float amount) + : base(KnownFilterMatrices.CreateLightnessFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index fdfaa9cb07..3c150d7ebf 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -19,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) => - new LomographProcessor(this, source, sourceRectangle); + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) => + new LomographProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index 7d3a5bbc0a..8e3759fba8 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { @@ -18,18 +17,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public LomographProcessor(LomographProcessor definition, Image source, Rectangle sourceRectangle) - : base(definition, source, sourceRectangle) + public LomographProcessor(Configuration configuration, LomographProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition, source, sourceRectangle) { } /// protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkGreen).Execute(this.Source, this.SourceRectangle); + new VignetteProcessor(VeryDarkGreen).Execute(this.Configuration, this.Source, this.SourceRectangle); base.AfterImageApply(); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index c8527a29cf..a5cf268625 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -19,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) => - new PolaroidProcessor(this, source, sourceRectangle); + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) => + new PolaroidProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index f7ab1a1ec2..24ee162966 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { @@ -14,25 +13,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters where TPixel : struct, IPixel { private static readonly Color LightOrange = Color.FromRgba(255, 153, 102, 128); - private static readonly Color VeryDarkOrange = Color.FromRgb(102, 34, 0); /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public PolaroidProcessor(PolaroidProcessor definition, Image source, Rectangle sourceRectangle) - : base(definition, source, sourceRectangle) + public PolaroidProcessor(Configuration configuration, PolaroidProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition, source, sourceRectangle) { } /// protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkOrange).Execute(this.Source, this.SourceRectangle); - new GlowProcessor(LightOrange, this.Source.Width / 4F).Execute(this.Source, this.SourceRectangle); + new VignetteProcessor(VeryDarkOrange).Execute(this.Configuration, this.Source, this.SourceRectangle); + new GlowProcessor(LightOrange, this.Source.Width / 4F).Execute(this.Configuration, this.Source, this.SourceRectangle); base.AfterImageApply(); } } diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs index 554a4b8860..ad8051e6bc 100644 --- a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -16,12 +15,13 @@ namespace SixLabors.ImageSharp.Processing.Processors /// the processing algorithm on an . /// /// The pixel type. + /// The configuration which allows altering default behaviour or extending the library. /// The source image. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// /// The - ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index fb7a6a4d9d..a9d5b20ec3 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -19,12 +18,13 @@ namespace SixLabors.ImageSharp.Processing.Processors /// the processing algorithm on an . /// /// The pixel type. + /// The configuration which allows altering default behaviour or extending the library. /// The source image. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// /// The - IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index ce8ed813b5..6f486e74b5 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -13,18 +12,21 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Executes the processor against the given source image and rectangle bounds. /// /// The processor. + /// The configuration which allows altering default behaviour or extending the library. /// The source image. /// The source bounds. - public static void Execute(this IImageProcessor processor, Image source, Rectangle sourceRectangle) - => source.AcceptVisitor(new ExecuteVisitor(processor, sourceRectangle)); + public static void Execute(this IImageProcessor processor, Configuration configuration, Image source, Rectangle sourceRectangle) + => source.AcceptVisitor(new ExecuteVisitor(configuration, processor, sourceRectangle)); private class ExecuteVisitor : IImageVisitor { + private readonly Configuration configuration; private readonly IImageProcessor processor; private readonly Rectangle sourceRectangle; - public ExecuteVisitor(IImageProcessor processor, Rectangle sourceRectangle) + public ExecuteVisitor(Configuration configuration, IImageProcessor processor, Rectangle sourceRectangle) { + this.configuration = configuration; this.processor = processor; this.sourceRectangle = sourceRectangle; } @@ -32,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Visit(Image image) where TPixel : struct, IPixel { - using (IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle)) + using (IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(this.configuration, image, this.sourceRectangle)) { processorImpl.Execute(); } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 3e46e3c087..be8bc8e122 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -2,9 +2,7 @@ // 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.Processors { @@ -19,13 +17,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The source for the current processor instance. /// The source area to process for the current processor instance. - protected ImageProcessor(Image source, Rectangle sourceRectangle) + protected ImageProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { + this.Configuration = configuration; this.Source = source; this.SourceRectangle = sourceRectangle; - this.Configuration = this.Source.GetConfiguration(); } /// @@ -94,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public virtual void Dispose() + public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index af3a336a4d..1fdb10661c 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// @@ -17,14 +15,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. public AdaptiveHistogramEqualizationProcessor( int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int numberOfTiles) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimit) { this.NumberOfTiles = numberOfTiles; } @@ -35,12 +33,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public int NumberOfTiles { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new AdaptiveHistogramEqualizationProcessor( + configuration, this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, + this.ClipLimit, this.NumberOfTiles, source, sourceRectangle); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 4cda4030fd..b5b8cfe568 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -11,8 +11,6 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -27,21 +25,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. /// The source for the current processor instance. /// The source area to process for the current processor instance. public AdaptiveHistogramEqualizationProcessor( + Configuration configuration, int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int tiles, Image source, Rectangle sourceRectangle) - : base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) + : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); @@ -512,7 +512,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization if (processor.ClipHistogramEnabled) { - processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); + processor.ClipHistogram(histogram, processor.ClipLimit); } Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs index 3ff001c522..ff8a6b73d9 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// @@ -16,14 +14,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. public AdaptiveHistogramEqualizationSlidingWindowProcessor( int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int numberOfTiles) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimit) { this.NumberOfTiles = numberOfTiles; } @@ -34,15 +32,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public int NumberOfTiles { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new AdaptiveHistogramEqualizationSlidingWindowProcessor( + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new AdaptiveHistogramEqualizationSlidingWindowProcessor( + configuration, this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, + this.ClipLimit, this.NumberOfTiles, source, sourceRectangle); - } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index 24ac5ccef2..987e4e392f 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -11,8 +11,6 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -26,21 +24,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. /// The source for the current processor instance. /// The source area to process for the current processor instance. public AdaptiveHistogramEqualizationSlidingWindowProcessor( + Configuration configuration, int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int tiles, Image source, Rectangle sourceRectangle) - : base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) + : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization { // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. histogram.CopyTo(histogramCopy); - this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixelInTile); + this.ClipHistogram(histogramCopy, this.ClipLimit); } // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. @@ -349,7 +349,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization { for (int idx = 0; idx < length; idx++) { - int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + int luminance = ImageMaths.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); Unsafe.Add(ref histogramBase, luminance)++; } } @@ -366,7 +366,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization { for (int idx = 0; idx < length; idx++) { - int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + int luminance = ImageMaths.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); Unsafe.Add(ref histogramBase, luminance)--; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index dab101fcc2..3b984578b5 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// @@ -15,21 +13,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// The number of luminance levels. /// A value indicating whether to clip the histogram bins at a specific value. - /// The histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit) + : base(luminanceLevels, clipHistogram, clipLimit) { } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new GlobalHistogramEqualizationProcessor( + public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new GlobalHistogramEqualizationProcessor( + configuration, this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, + this.ClipLimit, source, sourceRectangle); - } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 6ae6882479..ff34457fbb 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -8,11 +8,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -26,21 +24,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The source for the current processor instance. /// The source area to process for the current processor instance. public GlobalHistogramEqualizationProcessor( + Configuration configuration, int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, Image source, Rectangle sourceRectangle) - : base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) + : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { } @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization Span histogram = histogramBuffer.GetSpan(); if (this.ClipHistogramEnabled) { - this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); + this.ClipHistogram(histogram, this.ClipLimit); } // Calculate the cumulative distribution function, which will map each input pixel to a new value. diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 8ddb4834d9..b55b725a6b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -20,7 +20,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Gets or sets the number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. Defaults to 256. + /// or 65536 for 16-bit grayscale images. + /// Defaults to 256. /// public int LuminanceLevels { get; set; } = 256; @@ -32,14 +33,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogram { get; set; } = false; /// - /// Gets or sets the histogram clip limit in percent of the total pixels in a tile. Histogram bins which exceed this limit, will be capped at this value. - /// Defaults to 0.035f. + /// Gets or sets the histogram clip limit. Adaptive histogram equalization may cause noise to be amplified in near constant + /// regions. To reduce this problem, histogram bins which exceed a given limit will be capped at this value. The exceeding values + /// will be redistributed equally to all other bins. The clipLimit depends on the size of the tiles the image is split into + /// and therefore the image size itself. + /// Defaults to 350. /// - public float ClipLimitPercentage { get; set; } = 0.035f; + /// For more information, see also: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE + public int ClipLimit { get; set; } = 350; /// - /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. Defaults to 10. + /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// Defaults to 8. /// - public int NumberOfTiles { get; set; } = 10; + public int NumberOfTiles { get; set; } = 8; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index 01a687ac5c..8bd619095a 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -17,12 +16,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicates, if histogram bins should be clipped. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit) { this.LuminanceLevels = luminanceLevels; this.ClipHistogram = clipHistogram; - this.ClipLimitPercentage = clipLimitPercentage; + this.ClipLimit = clipLimit; } /// @@ -36,12 +35,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogram { get; } /// - /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// - public float ClipLimitPercentage { get; } + public int ClipLimit { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public abstract IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; /// @@ -60,14 +59,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new GlobalHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage); + options.ClipLimit); break; case HistogramEqualizationMethod.AdaptiveTileInterpolation: processor = new AdaptiveHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage, + options.ClipLimit, options.NumberOfTiles); break; @@ -75,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage, + options.ClipLimit, options.NumberOfTiles); break; @@ -83,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new GlobalHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage); + options.ClipLimit); break; } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs index f8515ece6f..d7d72d4c8b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -2,12 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -23,27 +21,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicates, if histogram bins should be clipped. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The source for the current processor instance. /// The source area to process for the current processor instance. protected HistogramEqualizationProcessor( + Configuration configuration, int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + : base(configuration, source, sourceRectangle) { Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); - Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); + Guard.MustBeGreaterThan(clipLimit, 1, nameof(clipLimit)); this.LuminanceLevels = luminanceLevels; this.luminanceLevelsFloat = luminanceLevels; this.ClipHistogramEnabled = clipHistogram; - this.ClipLimitPercentage = clipLimitPercentage; + this.ClipLimit = clipLimit; } /// @@ -57,9 +57,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogramEnabled { get; } /// - /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// - public float ClipLimitPercentage { get; } + public int ClipLimit { get; } /// /// Calculates the cumulative distribution function. @@ -96,11 +96,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// the values over the clip limit to all other bins equally. /// /// The histogram to apply the clipping. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The numbers of pixels inside the tile. - public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) + /// Histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + public void ClipHistogram(Span histogram, int clipLimit) { - int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); int sumOverClip = 0; ref int histogramBase = ref MemoryMarshal.GetReference(histogram); @@ -114,6 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } } + // Redistribute the clipped pixels over all bins of the histogram. int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; if (addToEachBin > 0) { @@ -122,6 +121,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization Unsafe.Add(ref histogramBase, i) += addToEachBin; } } + + int residual = sumOverClip - (addToEachBin * this.LuminanceLevels); + if (residual != 0) + { + int residualStep = Math.Max(this.LuminanceLevels / residual, 1); + for (int i = 0; i < this.LuminanceLevels && residual > 0; i += residualStep, residual--) + { + ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); + histogramLevel++; + } + } } /// @@ -133,16 +143,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) { var vector = sourcePixel.ToVector4(); - return GetLuminance(ref vector, luminanceLevels); + return ImageMaths.GetBT709Luminance(ref vector, luminanceLevels); } - - /// - /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. - /// - /// The vector to get the luminance from - /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) - [MethodImpl(InliningOptions.ShortMethod)] - public static int GetLuminance(ref Vector4 vector, int luminanceLevels) - => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index 4b4c537277..c9123bbbfb 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -14,9 +13,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// - /// The to set the background color to. /// The options defining blending algorithm and amount. - public BackgroundColorProcessor(Color color, GraphicsOptions options) + /// The to set the background color to. + public BackgroundColorProcessor(GraphicsOptions options, Color color) { this.Color = color; this.GraphicsOptions = options; @@ -33,10 +32,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays public Color Color { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new BackgroundColorProcessor(this, source, sourceRectangle); - } + => new BackgroundColorProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index 2459b47069..1c974612e9 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -5,10 +5,9 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -24,11 +23,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public BackgroundColorProcessor(BackgroundColorProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public BackgroundColorProcessor(Configuration configuration, BackgroundColorProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 0958e3aa9e..4f7ce7ba7b 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -24,10 +22,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// - /// The color or the glow. /// The options effecting blending and composition. - public GlowProcessor(Color color, GraphicsOptions options) - : this(color, 0, options) + /// The color or the glow. + public GlowProcessor(GraphicsOptions options, Color color) + : this(options, color, 0) { } @@ -37,17 +35,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// The color or the glow. /// The radius of the glow. internal GlowProcessor(Color color, ValueSize radius) - : this(color, radius, GraphicsOptions.Default) + : this(new GraphicsOptions(), color, radius) { } /// /// Initializes a new instance of the class. /// + /// The options effecting blending and composition. /// The color or the glow. /// The radius of the glow. - /// The options effecting blending and composition. - internal GlowProcessor(Color color, ValueSize radius, GraphicsOptions options) + internal GlowProcessor(GraphicsOptions options, Color color, ValueSize radius) { this.GlowColor = color; this.Radius = radius; @@ -67,13 +65,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Gets the the radius. /// - internal ValueSize Radius { get; } + internal ValueSize Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new GlowProcessor(this, source, sourceRectangle); - } + => new GlowProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 756e8647ba..d6aa6f8943 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -6,10 +6,9 @@ using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -21,17 +20,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays where TPixel : struct, IPixel { private readonly PixelBlender blender; - private readonly GlowProcessor definition; /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public GlowProcessor(GlowProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public GlowProcessor(Configuration configuration, GlowProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; this.blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 2365318f3d..9915a5f526 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -17,16 +15,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// The color of the vignette. public VignetteProcessor(Color color) - : this(color, GraphicsOptions.Default) + : this(new GraphicsOptions(), color) { } /// /// Initializes a new instance of the class. /// - /// The color of the vignette. /// The options effecting blending and composition. - public VignetteProcessor(Color color, GraphicsOptions options) + /// The color of the vignette. + public VignetteProcessor(GraphicsOptions options, Color color) { this.VignetteColor = color; this.GraphicsOptions = options; @@ -35,11 +33,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// + /// The options effecting blending and composition. /// The color of the vignette. /// The x-radius. /// The y-radius. - /// The options effecting blending and composition. - internal VignetteProcessor(Color color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) + internal VignetteProcessor(GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY) { this.VignetteColor = color; this.RadiusX = radiusX; @@ -68,10 +66,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays internal ValueSize RadiusY { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new VignetteProcessor(this, source, sourceRectangle); - } + => new VignetteProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 8569410d22..fd782261b2 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -6,10 +6,9 @@ using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -27,11 +26,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public VignetteProcessor(VignetteProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public VignetteProcessor(Configuration configuration, VignetteProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; this.blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index e6ffecc84d..71013548b8 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -1,11 +1,12 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -31,7 +32,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The vector representation of the image palette. /// - private Vector4[] paletteVector; + private IMemoryOwner paletteVector; + + private bool isDisposed; /// /// Initializes a new instance of the class. @@ -80,8 +83,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public bool Dither { get; } /// - public virtual void Dispose() + public void Dispose() { + this.Dispose(true); + GC.SuppressFinalize(this); } /// @@ -103,11 +108,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Collect the palette. Required before the second pass runs. ReadOnlyMemory palette = this.GetPalette(); - this.paletteVector = new Vector4[palette.Length]; + this.paletteVector = image.Configuration.MemoryAllocator.Allocate(palette.Length); PixelOperations.Instance.ToVector4( image.Configuration, palette.Span, - (Span)this.paletteVector, + this.paletteVector.Memory.Span, PixelConversionModifiers.Scale); var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); @@ -129,6 +134,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return quantizedFrame; } + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose managed and unmanaged objects. + protected virtual void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.paletteVector?.Dispose(); + } + + this.paletteVector = null; + + this.isDisposed = true; + } + /// /// Execute the first pass through the pixels in the image to create the palette. /// @@ -161,7 +187,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Retrieve the palette for the quantized image. /// /// - /// + /// /// protected abstract ReadOnlyMemory GetPalette(); @@ -173,12 +199,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization protected byte GetTransparentIndex() { // Transparent pixels are much more likely to be found at the end of a palette. - int paletteVectorLengthMinus1 = this.paletteVector.Length - 1; + Span paletteVectorSpan = this.paletteVector.Memory.Span; + ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); + + int paletteVectorLengthMinus1 = paletteVectorSpan.Length - 1; int index = paletteVectorLengthMinus1; for (int i = paletteVectorLengthMinus1; i >= 0; i--) { - ref Vector4 candidate = ref this.paletteVector[i]; + ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, i); if (candidate.Equals(default)) { index = i; @@ -211,10 +240,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization float leastDistance = float.MaxValue; Vector4 vector = pixel.ToScaledVector4(); float epsilon = Constants.EpsilonSquared; + Span paletteVectorSpan = this.paletteVector.Memory.Span; + ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); - for (int index = 0; index < this.paletteVector.Length; index++) + for (int index = 0; index < paletteVectorSpan.Length; index++) { - ref Vector4 candidate = ref this.paletteVector[index]; + ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, index); float distance = Vector4.DistanceSquared(vector, candidate); // Greater... Move on. @@ -239,4 +270,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return result; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs index 85a4d20295..393cb5f602 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height); } output[(y * source.Width) + x] = pixelValue; @@ -571,4 +571,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs index 265c343e68..f774f80be2 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height); } output[(y * source.Width) + x] = pixelValue; @@ -98,4 +98,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization [MethodImpl(MethodImplOptions.AggressiveInlining)] private byte QuantizePixel(ref TPixel pixel) => this.GetClosestPixel(ref pixel); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs index 8cc14da675..8e1dffeede 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { @@ -26,10 +25,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public IQuantizer Quantizer { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new QuantizeProcessor(this.Quantizer, source, sourceRectangle); - } + => new QuantizeProcessor(configuration, this.Quantizer, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index 9309467229..5e732982c3 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { @@ -21,11 +20,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The quantizer used to reduce the color palette. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public QuantizeProcessor(IQuantizer quantizer, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public QuantizeProcessor(Configuration configuration, IQuantizer quantizer, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { Guard.NotNull(quantizer, nameof(quantizer)); this.quantizer = quantizer; diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index cbea82c1f3..4938f0e127 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 87d696dc91..9b5e894278 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; // TODO: Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case? // (T, R, G, B, A, M2) could be grouped together! Investigate a ColorMoment struct. @@ -117,6 +116,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// private Box[] colorCube; + private bool isDisposed; + /// /// Initializes a new instance of the class. /// @@ -158,15 +159,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - this.vwt?.Dispose(); - this.vmr?.Dispose(); - this.vmg?.Dispose(); - this.vmb?.Dispose(); - this.vma?.Dispose(); - this.m2?.Dispose(); - this.tag?.Dispose(); + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.vwt?.Dispose(); + this.vmr?.Dispose(); + this.vmg?.Dispose(); + this.vmb?.Dispose(); + this.vma?.Dispose(); + this.m2?.Dispose(); + this.tag?.Dispose(); + } this.vwt = null; this.vmr = null; @@ -175,6 +184,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.vma = null; this.m2 = null; this.tag = null; + + this.isDisposed = true; + base.Dispose(true); } internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); @@ -260,7 +272,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 6ca844fae6..849f061669 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -41,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) - => new AffineTransformProcessor(this, source, sourceRectangle); + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new AffineTransformProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 97b8b009b5..1b9ff82bf9 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -4,9 +4,8 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -24,11 +23,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public AffineTransformProcessor(AffineTransformProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public AffineTransformProcessor(Configuration configuration, AffineTransformProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.targetSize = definition.TargetDimensions; this.transformMatrix = definition.TransformMatrix; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index eef7643da3..a059fb819a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -12,10 +11,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public sealed class AutoOrientProcessor : IImageProcessor { /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new AutoOrientProcessor(source, sourceRectangle); - } + => new AutoOrientProcessor(configuration, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index b9952ac8fe..90edcfac51 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -19,10 +18,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public AutoOrientProcessor(Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public AutoOrientProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { } @@ -34,33 +34,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms switch (orientation) { case OrientationMode.TopRight: - new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.LeftTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); - new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.RightTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.RightBottom: - new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); - new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.LeftBottom: - new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle); break; case OrientationMode.Unknown: @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return OrientationMode.Unknown; } - ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + IExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); if (value is null) { return OrientationMode.Unknown; diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 245a542084..9aa21e4dcd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// @@ -32,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Rectangle CropRectangle { get; } /// - public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) - => new CropProcessor(this, source, sourceRectangle); + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new CropProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 1bbdd0a161..a286e8fa26 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -3,9 +3,8 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -21,11 +20,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The . /// The source for the current processor instance. /// The source area to process for the current processor instance. - public CropProcessor(CropProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public CropProcessor(Configuration configuration, CropProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) => this.cropRectangle = definition.CropRectangle; /// @@ -47,10 +47,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Rectangle bounds = this.cropRectangle; // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings - = this.Configuration - .GetParallelSettings() - .MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration) + .MultiplyMinimumPixelsPerTask(4); ParallelHelper.IterateRows( bounds, diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 22eecb598e..d5aaaf515e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -38,10 +37,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Threshold { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new EntropyCropProcessor(this, source, sourceRectangle); - } + => new EntropyCropProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index 2b900ee360..74d719fbe7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -21,11 +20,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The . /// The source for the current processor instance. /// The source area to process for the current processor instance. - public EntropyCropProcessor(EntropyCropProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public EntropyCropProcessor(Configuration configuration, EntropyCropProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } @@ -42,16 +42,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = this.Source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Execute(temp, this.SourceRectangle); + new SobelProcessor(false).Execute(this.Configuration, temp, this.SourceRectangle); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.definition.Threshold).Execute(temp, this.SourceRectangle); + new BinaryThresholdProcessor(this.definition.Threshold).Execute(this.Configuration, temp, this.SourceRectangle); // Search for the first white pixels rectangle = ImageMaths.GetFilteredBoundingRectangle(temp.Frames.RootFrame, 0); } - new CropProcessor(rectangle, this.Source.Size()).Execute(this.Source, this.SourceRectangle); + new CropProcessor(rectangle, this.Source.Size()).Execute(this.Configuration, this.Source, this.SourceRectangle); base.BeforeImageApply(); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index e2364e180f..60e22e2d0e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -15,10 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Initializes a new instance of the class. /// /// The used to perform flipping. - public FlipProcessor(FlipMode flipMode) - { - this.FlipMode = flipMode; - } + public FlipProcessor(FlipMode flipMode) => this.FlipMode = flipMode; /// /// Gets the used to perform flipping. @@ -26,10 +22,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public FlipMode FlipMode { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new FlipProcessor(this, source, sourceRectangle); - } + => new FlipProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs index 9374af476b..d3afc72057 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -5,9 +5,8 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -23,11 +22,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The . /// The source for the current processor instance. /// The source area to process for the current processor instance. - public FlipProcessor(FlipProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public FlipProcessor(Configuration configuration, FlipProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.definition = definition; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index d91db9a72b..d8a9c3ed96 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -41,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) - => new ProjectiveTransformProcessor(this, source, sourceRectangle); + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new ProjectiveTransformProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 68bfd817e5..56df606a77 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -5,9 +5,8 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -25,11 +24,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public ProjectiveTransformProcessor(Configuration configuration, ProjectiveTransformProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.targetSize = definition.TargetDimensions; this.transformMatrix = definition.TransformMatrix; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index eacd3834f1..de44d32e4b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs index 6d6e22a6a5..be2546369e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index 9abbb66e3a..1b653a92c3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -56,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.DestinationLength = destinationLength; this.MaxDiameter = (radius * 2) + 1; this.data = memoryAllocator.Allocate2D(this.MaxDiameter, bufferHeight, AllocationOptions.Clean); - this.pinHandle = this.data.Memory.Pin(); + this.pinHandle = this.data.GetMemory().Pin(); this.kernels = new ResizeKernel[destinationLength]; this.tempValues = new double[this.MaxDiameter]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index ccaa1ef9e5..ec1f94c143 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// @@ -55,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) - => new ResizeProcessor(this, source, sourceRectangle); + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new ResizeProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 78e471ad62..2e94f88aca 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -4,11 +4,9 @@ using System; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -33,8 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private ResizeKernelMap horizontalKernelMap; private ResizeKernelMap verticalKernelMap; - public ResizeProcessor(ResizeProcessor definition, Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { this.targetWidth = definition.TargetWidth; this.targetHeight = definition.TargetHeight; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 00a8cfbf3d..4f5faa38e3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -9,8 +9,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -115,13 +113,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void FillDestinationPixels(RowInterval rowInterval, Buffer2D destination) { Span tempColSpan = this.tempColumnBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); for (int y = rowInterval.Min; y < rowInterval.Max; y++) { // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - this.targetOrigin.Y); - if (kernel.StartIndex + kernel.Length > this.currentWindow.Max) + while (kernel.StartIndex + kernel.Length > this.currentWindow.Max) { this.Slide(); } @@ -129,8 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); int top = kernel.StartIndex - this.currentWindow.Min; - - ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top]; + ref Vector4 fpBase = ref transposedFirstPassBufferSpan[top]; for (int x = 0; x < this.destWidth; x++) { @@ -167,6 +165,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void CalculateFirstPassValues(RowInterval calculationInterval) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); + for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -177,17 +177,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms tempRowSpan, this.conversionModifiers); - // Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); - ref Vector4 firstPassBaseRef = ref this.transposedFirstPassBuffer.Span[y - this.currentWindow.Min]; + // optimization for: + // Span firstPassSpan = transposedFirstPassBufferSpan.Slice(y - this.currentWindow.Min); + ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - this.currentWindow.Min]; for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); + // optimization for: // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan); } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 7d6ec0e08e..aae66e9eac 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -46,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Degrees { get; } /// - public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) - => new RotateProcessor(this, source, sourceRectangle); + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + => new RotateProcessor(configuration, this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index 1ed4c362c5..8f1cf28ce4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -4,10 +4,9 @@ using System; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -21,11 +20,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The defining the processor parameters. /// The source for the current processor instance. /// The source area to process for the current processor instance. - public RotateProcessor(RotateProcessor definition, Image source, Rectangle sourceRectangle) - : base(definition, source, sourceRectangle) + public RotateProcessor(Configuration configuration, RotateProcessor definition, Image source, Rectangle sourceRectangle) + : base(configuration, definition, source, sourceRectangle) { this.Degrees = definition.Degrees; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index fb2114e03c..4d07333345 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs index 573120888f..a0d44cb7a1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs index 1f0c6ebc86..3a0a7e54eb 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -16,10 +15,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Initializes a new instance of the class. /// + /// The configuration which allows altering default behaviour or extending the library. /// The source for the current processor instance. /// The source area to process for the current processor instance. - protected TransformProcessor(Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + protected TransformProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs index 00c1227a6c..14eeca7cc3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -25,34 +25,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - // Removing the previously stored value allows us to set a value with our own data tag if required. + // Only set the value if it already exists. 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); - } + profile.SetValue(ExifTag.PixelXDimension, 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); - } + profile.SetValue(ExifTag.PixelYDimension, image.Height); } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs index 794645550e..e0fb554385 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index c29941d071..0ff693d81c 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Numerics; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/src/ImageSharp/Processing/ResizeOptions.cs b/src/ImageSharp/Processing/ResizeOptions.cs index ef88dc35b3..b54d2eae1c 100644 --- a/src/ImageSharp/Processing/ResizeOptions.cs +++ b/src/ImageSharp/Processing/ResizeOptions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { diff --git a/tests/CodeCoverage/CodeCoverage.cmd b/tests/CodeCoverage/CodeCoverage.cmd deleted file mode 100644 index 01e342b3d2..0000000000 --- a/tests/CodeCoverage/CodeCoverage.cmd +++ /dev/null @@ -1,21 +0,0 @@ -@echo off - - -cd tests\CodeCoverage - -nuget restore packages.config -PackagesDirectory . - -cd .. -cd .. - -dotnet restore ImageSharp.sln -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 -c Release -f netcoreapp2.1 /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% - -SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH% -pip install codecov -codecov -f "ImageSharp.Coverage.xml" \ No newline at end of file diff --git a/tests/CodeCoverage/packages.config b/tests/CodeCoverage/packages.config deleted file mode 100644 index 973b7f81b4..0000000000 --- a/tests/CodeCoverage/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 97bd9b6e7c..07d3332760 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -13,18 +13,29 @@ $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props tests + false - CS0618;$(NoWarn) + $(MSBuildThisFileDirectory)..\shared-infrastructure\SixLabors.Tests.ruleset + + $(NoWarn);CS0618 - + + + + + + + + + diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index f8a4936e23..bacaaa7093 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -16,4 +16,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs index 1ab5ed3099..6be1998fba 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeBmp.cs @@ -1,13 +1,12 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -using CoreSize = SixLabors.Primitives.Size; using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs { @@ -31,7 +30,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public string TestImage { get; set; } [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] - public Size BmpSystemDrawing() + public SDSize BmpSystemDrawing() { using (var memoryStream = new MemoryStream(this.bmpBytes)) { @@ -43,15 +42,15 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs } [Benchmark(Description = "ImageSharp Bmp")] - public CoreSize BmpCore() + public Size BmpCore() { using (var memoryStream = new MemoryStream(this.bmpBytes)) { using (var image = Image.Load(memoryStream)) { - return new CoreSize(image.Width, image.Height); + return new Size(image.Width, image.Height); } } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs index cc946e05ad..e4723d3a06 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeFilteredPng.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -using CoreSize = SixLabors.Primitives.Size; +using CoreSize = SixLabors.ImageSharp.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs { diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeGif.cs index be7e853000..82dd57c299 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeGif.cs @@ -1,13 +1,12 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -using CoreSize = SixLabors.Primitives.Size; using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs { @@ -31,7 +30,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public string TestImage { get; set; } [Benchmark(Baseline = true, Description = "System.Drawing Gif")] - public Size GifSystemDrawing() + public SDSize GifSystemDrawing() { using (var memoryStream = new MemoryStream(this.gifBytes)) { @@ -43,13 +42,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs } [Benchmark(Description = "ImageSharp Gif")] - public CoreSize GifCore() + public Size GifCore() { using (var memoryStream = new MemoryStream(this.gifBytes)) { using (var image = Image.Load(memoryStream)) { - return new CoreSize(image.Width, image.Height); + return new Size(image.Width, image.Height); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs index a19d8fa91e..b69dd36d78 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodePng.cs @@ -1,13 +1,12 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -using CoreSize = SixLabors.Primitives.Size; using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs { @@ -31,7 +30,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs } [Benchmark(Baseline = true, Description = "System.Drawing Png")] - public Size PngSystemDrawing() + public SDSize PngSystemDrawing() { using (var memoryStream = new MemoryStream(this.pngBytes)) { @@ -43,7 +42,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs } [Benchmark(Description = "ImageSharp Png")] - public CoreSize PngCore() + public Size PngCore() { using (var memoryStream = new MemoryStream(this.pngBytes)) { diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs new file mode 100644 index 0000000000..527b6bb8bc --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTga.cs @@ -0,0 +1,91 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using System.IO; +using System.Threading; +using BenchmarkDotNet.Attributes; + +using ImageMagick; +using Pfim; +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs +{ + [Config(typeof(Config.ShortClr))] + public class DecodeTga : BenchmarkBase + { + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + private readonly PfimConfig pfimConfig = new PfimConfig(allocator: new PfimAllocator()); + private byte[] data; + + [Params(TestImages.Tga.Bit24)] + public string TestImage { get; set; } + + [GlobalSetup] + public void SetupData() + { + this.data = File.ReadAllBytes(this.TestImageFullPath); + } + + [Benchmark(Baseline = true, Description = "ImageMagick Tga")] + public int TgaImageMagick() + { + var settings = new MagickReadSettings { Format = MagickFormat.Tga }; + using (var image = new MagickImage(new MemoryStream(this.data), settings)) + { + return image.Width; + } + } + + [Benchmark(Description = "ImageSharp Tga")] + public int TgaCore() + { + using (var image = Image.Load(this.data, new TgaDecoder())) + { + return image.Width; + } + } + + [Benchmark(Description = "Pfim Tga")] + public int TgaPfim() + { + using (var image = Targa.Create(this.data, this.pfimConfig)) + { + return image.Width; + } + } + + private class PfimAllocator : IImageAllocator + { + private int rented; + private readonly ArrayPool shared = ArrayPool.Shared; + + public byte[] Rent(int size) + { + return this.shared.Rent(size); + } + + public void Return(byte[] data) + { + Interlocked.Decrement(ref this.rented); + this.shared.Return(data); + } + + public int Rented => this.rented; + } + + // RESULTS (07/01/2020) + //| Method | Runtime | TestImage | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated | + //|------------------ |-------------- |-------------------- |-------------:|-------------:|-----------:|------:|-------:|------:|------:|----------:| + //| 'ImageMagick Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 1,778.965 us | 1,711.088 us | 93.7905 us | 1.000 | 1.9531 | - | - | 13668 B | + //| 'ImageSharp Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 38.659 us | 6.886 us | 0.3774 us | 0.022 | 0.3052 | - | - | 1316 B | + //| 'Pfim Tga' | .NET 4.7.2 | Tga/targa_24bit.tga | 6.752 us | 10.268 us | 0.5628 us | 0.004 | 0.0687 | - | - | 313 B | + //| | | | | | | | | | | | + //| 'ImageMagick Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 1,407.585 us | 124.215 us | 6.8087 us | 1.000 | 1.9531 | - | - | 13307 B | + //| 'ImageSharp Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 17.958 us | 9.352 us | 0.5126 us | 0.013 | 0.2747 | - | - | 1256 B | + //| 'Pfim Tga' | .NET Core 2.1 | Tga/targa_24bit.tga | 5.645 us | 2.279 us | 0.1249 us | 0.004 | 0.0610 | - | - | 280 B | + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodePng.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodePng.cs index 157dadd2c1..7bd1b80447 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodePng.cs @@ -1,9 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Drawing.Imaging; using System.IO; using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using SDImage = System.Drawing.Image; @@ -56,8 +57,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { using (var memoryStream = new MemoryStream()) { - this.bmpCore.SaveAsPng(memoryStream); + var encoder = new PngEncoder { FilterMethod = PngFilterMethod.None }; + this.bmpCore.SaveAsPng(memoryStream, encoder); } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs new file mode 100644 index 0000000000..ddcbec218e --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTga.cs @@ -0,0 +1,54 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using BenchmarkDotNet.Attributes; + +using ImageMagick; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs +{ + [Config(typeof(Config.ShortClr))] + public class EncodeTga : BenchmarkBase + { + private MagickImage tgaMagick; + private Image tgaCore; + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [Params(TestImages.Tga.Bit24)] + public string TestImage { get; set; } + + [GlobalSetup] + public void ReadImages() + { + if (this.tgaCore == null) + { + this.tgaCore = Image.Load(TestImageFullPath); + this.tgaMagick = new MagickImage(this.TestImageFullPath); + } + } + + [Benchmark(Baseline = true, Description = "Magick Tga")] + public void BmpSystemDrawing() + { + using (var memoryStream = new MemoryStream()) + { + this.tgaMagick.Write(memoryStream, MagickFormat.Tga); + } + } + + [Benchmark(Description = "ImageSharp Tga")] + public void BmpCore() + { + using (var memoryStream = new MemoryStream()) + { + this.tgaCore.SaveAsBmp(memoryStream); + } + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index f40c15cc14..5b783dddca 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -1,11 +1,11 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using BenchmarkDotNet.Attributes; -using System.Drawing; using System.IO; +using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; +using SDSize = System.Drawing.Size; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark(Baseline = true, Description = "System.Drawing FULL")] - public Size JpegSystemDrawing() + public SDSize JpegSystemDrawing() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs index 99b071e59e..6a2040afc2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -1,18 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; -using CoreSize = SixLabors.Primitives.Size; using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg @@ -35,8 +35,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg public ShortClr() { this.Add( - // Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3), - Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3) + // Job.Default.With(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3), + Job.Default.With(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(2).WithIterationCount(3) ); } } @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] - public Size JpegSystemDrawing() + public SDSize JpegSystemDrawing() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { @@ -83,13 +83,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark(Description = "Decode Jpeg - ImageSharp")] - public CoreSize JpegImageSharp() + public Size JpegImageSharp() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { using (var image = Image.Load(memoryStream, new JpegDecoder { IgnoreMetadata = true })) { - return new CoreSize(image.Width, image.Height); + return new Size(image.Width, image.Height); } } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index bf694211de..6d4caa8436 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Diagnosers; + using BenchmarkDotNet.Environments; using SixLabors.ImageSharp.Tests; using CoreImage = ImageSharp.Image; @@ -36,7 +37,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public ShortClr() { this.Add( - Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithIterationCount(2) + Job.Default.With(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(1).WithIterationCount(2) ); } } diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 0543cbc50d..cb4fcbba18 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -1,8 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; namespace SixLabors.ImageSharp.Benchmarks @@ -19,10 +20,11 @@ namespace SixLabors.ImageSharp.Benchmarks public ShortClr() { this.Add( - Job.Clr.WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), - Job.Core.WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3) + Job.Default.With(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), + Job.Default.With(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), + Job.Default.With(CoreRuntime.Core21).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3) ); } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs deleted file mode 100644 index 8f4a7dfcb7..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.IO; -using System.Numerics; -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Benchmarks -{ - public class DrawBeziers : BenchmarkBase - { - [Benchmark(Baseline = true, Description = "System.Drawing Draw Beziers")] - public void DrawPathSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - - using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) - { - graphics.DrawBeziers(pen, new[] { - new PointF(10, 500), - new PointF(30, 10), - new PointF(240, 30), - new PointF(300, 500) - }); - } - - using (var stream = new MemoryStream()) - { - destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); - } - } - } - - [Benchmark(Description = "ImageSharp Draw Beziers")] - public void DrawLinesCore() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.DrawBeziers( - Rgba32.HotPink, - 10, - new Vector2(10, 500), - new Vector2(30, 10), - new Vector2(240, 30), - new Vector2(300, 500))); - - using (var stream = new MemoryStream()) - { - image.SaveAsBmp(stream); - } - } - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs deleted file mode 100644 index 43b7672c47..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.IO; -using System.Numerics; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Benchmarks -{ - public class DrawLines : BenchmarkBase - { - [Benchmark(Baseline = true, Description = "System.Drawing Draw Lines")] - public void DrawPathSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - - using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) - { - graphics.DrawLines(pen, new[] { - new PointF(10, 10), - new PointF(550, 50), - new PointF(200, 400) - }); - } - - using (var stream = new MemoryStream()) - { - destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); - } - } - } - - [Benchmark(Description = "ImageSharp Draw Lines")] - public void DrawLinesCore() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.DrawLines( - Rgba32.HotPink, - 10, - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400))); - - using (var stream = new MemoryStream()) - { - image.SaveAsBmp(stream); - } - } - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs deleted file mode 100644 index f20469b63d..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using BenchmarkDotNet.Attributes; -using System.IO; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Benchmarks -{ - public class DrawPolygon : BenchmarkBase - { - [Benchmark(Baseline = true, Description = "System.Drawing Draw Polygon")] - public void DrawPolygonSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) - { - graphics.DrawPolygon(pen, new[] { - new PointF(10, 10), - new PointF(550, 50), - new PointF(200, 400) - }); - } - - using (var stream = new MemoryStream()) - { - destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); - } - } - } - - [Benchmark(Description = "ImageSharp Draw Polygon")] - public void DrawPolygonCore() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.DrawPolygon( - Rgba32.HotPink, - 10, - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400))); - - using (var ms = new MemoryStream()) - { - image.SaveAsBmp(ms); - } - } - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs deleted file mode 100644 index 0982db3340..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using System.Linq; -using SixLabors.ImageSharp.Processing.Processors.Text; - -namespace SixLabors.ImageSharp.Benchmarks -{ - [MemoryDiagnoser] - public class DrawText : BenchmarkBase - { - [Params(10, 100)] - public int TextIterations { get; set; } - public string TextPhrase { get; set; } = "Hello World"; - public string TextToRender => string.Join(" ", Enumerable.Repeat(this.TextPhrase, this.TextIterations)); - - - [Benchmark(Baseline = true, Description = "System.Drawing Draw Text")] - public void DrawTextSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var font = new Font("Arial", 12, GraphicsUnit.Point)) - { - graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); - } - } - } - - [Benchmark(Description = "ImageSharp Draw Text - Cached Glyphs")] - public void DrawTextCore() - { - using (var image = new Image(800, 800)) - { - var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); - } - } - - [Benchmark(Description = "ImageSharp Draw Text - Nieve")] - public void DrawTextCoreOld() - { - using (var image = new Image(800, 800)) - { - var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); - } - - IImageProcessingContext DrawTextOldVersion( - IImageProcessingContext source, - TextGraphicsOptions options, - string text, - SixLabors.Fonts.Font font, - IBrush brush, - IPen pen, - SixLabors.Primitives.PointF location) - { - float dpiX = 72; - float dpiY = 72; - - var style = new SixLabors.Fonts.RendererOptions(font, dpiX, dpiY, location) - { - ApplyKerning = options.ApplyKerning, - TabWidth = options.TabWidth, - WrappingWidth = options.WrapTextWidth, - HorizontalAlignment = options.HorizontalAlignment, - VerticalAlignment = options.VerticalAlignment - }; - - Shapes.IPathCollection glyphs = Shapes.TextBuilder.GenerateGlyphs(text, style); - - var pathOptions = (GraphicsOptions)options; - if (brush != null) - { - source.Fill(pathOptions, brush, glyphs); - } - - if (pen != null) - { - source.Draw(pathOptions, pen, glyphs); - } - - return source; - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs deleted file mode 100644 index c5c1ba5ac1..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using System.Linq; -using SixLabors.ImageSharp.Processing.Processors.Text; - -namespace SixLabors.ImageSharp.Benchmarks -{ - [MemoryDiagnoser] - public class DrawTextOutline : BenchmarkBase - { - [Params(10, 100)] - public int TextIterations { get; set; } - public string TextPhrase { get; set; } = "Hello World"; - public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations)); - - [Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")] - public void DrawTextSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10)) - using (var font = new Font("Arial", 12, GraphicsUnit.Point)) - using (var gp = new GraphicsPath()) - { - gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); - graphics.DrawPath(pen, gp); - } - } - } - - [Benchmark(Description = "ImageSharp Draw Text Outline - Cached Glyphs")] - public void DrawTextCore() - { - using (var image = new Image(800, 800)) - { - var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); - } - } - - [Benchmark(Description = "ImageSharp Draw Text Outline - Nieve")] - public void DrawTextCoreOld() - { - using (var image = new Image(800, 800)) - { - var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate( - x => DrawTextOldVersion( - x, - new TextGraphicsOptions(true) { WrapTextWidth = 780 }, - TextToRender, - font, - null, - Processing.Pens.Solid(Rgba32.HotPink, 10), - new SixLabors.Primitives.PointF(10, 10))); - } - - IImageProcessingContext DrawTextOldVersion( - IImageProcessingContext source, - TextGraphicsOptions options, - string text, - SixLabors.Fonts.Font font, - IBrush brush, - IPen pen, - SixLabors.Primitives.PointF location) - { - var style = new SixLabors.Fonts.RendererOptions(font, options.DpiX, options.DpiY, location) - { - ApplyKerning = options.ApplyKerning, - TabWidth = options.TabWidth, - WrappingWidth = options.WrapTextWidth, - HorizontalAlignment = options.HorizontalAlignment, - VerticalAlignment = options.VerticalAlignment - }; - - Shapes.IPathCollection glyphs = Shapes.TextBuilder.GenerateGlyphs(text, style); - - var pathOptions = (GraphicsOptions)options; - if (brush != null) - { - source.Fill(pathOptions, brush, glyphs); - } - - if (pen != null) - { - source.Draw(pathOptions, pen, glyphs); - } - - return source; - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs deleted file mode 100644 index f33df7ec62..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.IO; -using System.Numerics; -using SixLabors.Shapes; -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Benchmarks -{ - public class FillPolygon : BenchmarkBase - { - private readonly Polygon shape; - - public FillPolygon() - { - this.shape = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400))); - } - - [Benchmark(Baseline = true, Description = "System.Drawing Fill Polygon")] - public void DrawSolidPolygonSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - - using (var graphics = Graphics.FromImage(destination)) - { - graphics.SmoothingMode = SmoothingMode.AntiAlias; - graphics.FillPolygon(System.Drawing.Brushes.HotPink, - new[] { - new Point(10, 10), - new Point(550, 50), - new Point(200, 400) - }); - - using (var stream = new MemoryStream()) - { - destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); - } - } - } - - [Benchmark(Description = "ImageSharp Fill Polygon")] - public void DrawSolidPolygonCore() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.FillPolygon( - Rgba32.HotPink, - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400))); - - using (var stream = new MemoryStream()) - { - image.SaveAsBmp(stream); - } - } - } - - [Benchmark(Description = "ImageSharp Fill Polygon - cached shape")] - public void DrawSolidPolygonCoreCached() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.Fill( - Rgba32.HotPink, - this.shape)); - - using (var stream = new MemoryStream()) - { - image.SaveAsBmp(stream); - } - } - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs deleted file mode 100644 index 531c540da7..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Numerics; -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using CoreRectangle = SixLabors.Primitives.Rectangle; -using CoreSize = SixLabors.Primitives.Size; - -namespace SixLabors.ImageSharp.Benchmarks -{ - public class FillRectangle : BenchmarkBase - { - [Benchmark(Baseline = true, Description = "System.Drawing Fill Rectangle")] - public Size FillRectangleSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.Default; - graphics.SmoothingMode = SmoothingMode.AntiAlias; - graphics.FillRectangle(System.Drawing.Brushes.HotPink, new Rectangle(10, 10, 190, 140)); - - return destination.Size; - } - } - - [Benchmark(Description = "ImageSharp Fill Rectangle")] - public CoreSize FillRectangleCore() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140))); - - return new CoreSize(image.Width, image.Height); - } - } - - [Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")] - public CoreSize FillPolygonCore() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.FillPolygon( - Rgba32.HotPink, - new Vector2(10, 10), - new Vector2(200, 10), - new Vector2(200, 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 deleted file mode 100644 index 411f8210a9..0000000000 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.IO; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -using CoreBrushes = SixLabors.ImageSharp.Processing.Brushes; - -namespace SixLabors.ImageSharp.Benchmarks -{ - public class FillWithPattern - { - [Benchmark(Baseline = true, Description = "System.Drawing Fill with Pattern")] - public void DrawPatternPolygonSystemDrawing() - { - using (var destination = new Bitmap(800, 800)) - using (var graphics = Graphics.FromImage(destination)) - { - graphics.SmoothingMode = SmoothingMode.AntiAlias; - - using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, System.Drawing.Color.HotPink)) - { - graphics.FillRectangle(brush, new Rectangle(0, 0, 800, 800)); // can't find a way to flood fill with a brush - } - - using (var stream = new MemoryStream()) - { - destination.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); - } - } - } - - [Benchmark(Description = "ImageSharp Fill with Pattern")] - public void DrawPatternPolygon3Core() - { - using (var image = new Image(800, 800)) - { - image.Mutate(x => x.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink))); - - using (var stream = new MemoryStream()) - { - image.SaveAsBmp(stream); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index 1f8961fcde..fe7fd2090c 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -5,7 +5,7 @@ using System; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp; namespace SixLabors.ImageSharp.Benchmarks.General { diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 14ad5635cd..60b1fde8e0 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,13 +1,14 @@ - + ImageSharp.Benchmarks Exe SixLabors.ImageSharp.Benchmarks - netcoreapp2.1;net472 - false + netcoreapp3.1;netcoreapp2.1;net472 false + + false @@ -16,15 +17,11 @@ + - + - - - - - diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index ce4e16c446..4241a12f6a 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -13,7 +13,7 @@ using SixLabors.ImageSharp.PixelFormats.PixelBlenders; namespace SixLabors.ImageSharp.Benchmarks { - using CoreSize = SixLabors.Primitives.Size; + using CoreSize = SixLabors.ImageSharp.Size; public class PorterDuffBulkVsPixel : BenchmarkBase { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs index 4fe7a365f3..e60ff8f02f 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -9,14 +9,16 @@ using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Processing; -using CoreSize = SixLabors.Primitives.Size; +using SDImage = System.Drawing.Image; +using SDSize = System.Drawing.Size; +using SDRectangle = System.Drawing.Rectangle; namespace SixLabors.ImageSharp.Benchmarks { public class Crop : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing Crop")] - public Size CropSystemDrawing() + public SDSize CropSystemDrawing() { using (var source = new Bitmap(800, 800)) using (var destination = new Bitmap(100, 100)) @@ -25,19 +27,19 @@ namespace SixLabors.ImageSharp.Benchmarks graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); + graphics.DrawImage(source, new SDRectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); return destination.Size; } } [Benchmark(Description = "ImageSharp Crop")] - public CoreSize CropResizeCore() + public Size CropResizeCore() { using (var image = new Image(800, 800)) { image.Mutate(x => x.Crop(100, 100)); - return new CoreSize(image.Width, image.Height); + return new Size(image.Width, image.Height); } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs new file mode 100644 index 0000000000..e5b12a0a29 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs @@ -0,0 +1,48 @@ +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp; + +namespace SixLabors.ImageSharp.Benchmarks.Samplers +{ + [Config(typeof(Config.ShortClr))] + public class Diffuse + { + [Benchmark] + public Size DoDiffuse() + { + using (var image = new Image(Configuration.Default, 800, 800, Rgba32.BlanchedAlmond)) + { + image.Mutate(x => x.Diffuse()); + + return image.Size(); + } + } + } +} + +// #### 25th October 2019 #### +// +// BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362 +// Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK = 3.0.100 +// +// [Host] : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT +// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.8.4018.0 +// Core : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT +// +// IterationCount=3 LaunchCount=1 WarmupCount=3 +// +// #### Before #### +// +// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------- |----- |-------- |----------:|---------:|---------:|------:|------:|------:|----------:| +// | DoDiffuse | Clr | Clr | 129.58 ms | 24.60 ms | 1.349 ms | - | - | - | 6 KB | +// | DoDiffuse | Core | Core | 92.63 ms | 89.78 ms | 4.921 ms | - | - | - | 4.58 KB | +// +// #### After #### +// +// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------- |----- |-------- |----------:|----------:|----------:|------:|------:|------:|----------:| +// | DoDiffuse | Clr | Clr | 124.93 ms | 33.297 ms | 1.8251 ms | - | - | - | 2 KB | +// | DoDiffuse | Core | Core | 89.63 ms | 9.895 ms | 0.5424 ms | - | - | - | 1.91 KB | diff --git a/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs b/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs index 69ff1549bd..244a0bb41d 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs @@ -4,7 +4,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; +using SixLabors.ImageSharp; namespace SixLabors.ImageSharp.Benchmarks.Samplers { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Skew.cs b/tests/ImageSharp.Benchmarks/Samplers/Skew.cs index 559e49704b..4061120b76 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Skew.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Skew.cs @@ -4,7 +4,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; +using SixLabors.ImageSharp; namespace SixLabors.ImageSharp.Benchmarks.Samplers { diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj deleted file mode 100644 index fc94668e11..0000000000 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - - SixLabors.ImageSharp.Sandbox46 - A cross-platform library for processing of image files written in C# - Exe - false - SixLabors.ImageSharp.Sandbox46 - win7-x64 - net472 - - - - - - - - - - - - - - - - - diff --git a/tests/ImageSharp.Sandbox46/README.md b/tests/ImageSharp.Sandbox46/README.md deleted file mode 100644 index b05afb8538..0000000000 --- a/tests/ImageSharp.Sandbox46/README.md +++ /dev/null @@ -1,24 +0,0 @@ -## Purpose -This project aims to workaround certain .NET Core tooling issues in Visual Studio based developer workflow at the time of it's creation (January 2017): -- .NET Core Performance profiling is not possible neither with Visual Studio nor with JetBrains profilers -- ~~JetBrains Unit Test explorer does not work with .NET Core projects~~ - -## How does it work? -- By referencing .NET 4.5 dll-s created by net45 target's of ImageSharp projects. NOTE: These are not project references! -- By including test classes (and utility classes) of the `ImageSharp.Tests` project using MSBUILD `` -- Compiling `ImageSharp.Sandbox46` should trigger the compilation of ImageSharp subprojects using a manually defined solution dependencies - -## How to profile unit tests - -#### 1. With Visual Studio 2015 Test Runner -- **Do not** build `ImageSharp.Tests` -- Build `ImageSharp.Sandbox46` -- Use the [context menu in Test Explorer](https://adamprescott.net/2012/12/12/performance-profiling-for-unit-tests/) - -NOTE: -There was no *Profile test* option in my VS Professional. Maybe things were messed by VS2017 RC installation. [This post suggests](http://stackoverflow.com/questions/32034375/profiling-tests-in-visual-studio-community-2015) it's necessary to own Premium or Ultimate edition of Visual Studio to profile tests. - -#### 2. With JetBrains ReSharper Ultimate -- The `Sandbox46` project is no longer needed here. The classic `ImageSharp.Tests` project can be discovered by Unit Test Explorer. -- You can use [context menus](https://www.jetbrains.com/resharper/features/unit_testing.html) from your test class, or from unit Test Exporer/Unit Test Sessions windows. -![Context Menu](https://www.jetbrains.com/resharper/features/screenshots/100/unit_testing_profiling.png) \ No newline at end of file diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj new file mode 100644 index 0000000000..99269e339a --- /dev/null +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -0,0 +1,27 @@ + + + + + ImageSharp.Tests.ProfilingSandbox + A cross-platform library for processing of image files written in C# + Exe + false + SixLabors.ImageSharp.Tests.ProfilingSandbox + win7-x64 + netcoreapp3.1;netcoreapp2.1;net472 + SixLabors.ImageSharp.Tests.ProfilingSandbox.Program + + false + + + + + + + + + + + + + diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs similarity index 88% rename from tests/ImageSharp.Sandbox46/Program.cs rename to tests/ImageSharp.Tests.ProfilingSandbox/Program.cs index 93fe74076e..f041e16eb5 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.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. -// +using System; +using SixLabors.ImageSharp.Tests.Formats.Jpg; using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; using SixLabors.ImageSharp.Tests.ProfilingBenchmarks; +using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Sandbox46 +namespace SixLabors.ImageSharp.Tests.ProfilingSandbox { - using System; - using SixLabors.ImageSharp.Tests.Formats.Jpg; - - using Xunit.Abstractions; - public class Program { private class ConsoleOutput : ITestOutputHelper diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/README.md b/tests/ImageSharp.Tests.ProfilingSandbox/README.md new file mode 100644 index 0000000000..43fdab9ef6 --- /dev/null +++ b/tests/ImageSharp.Tests.ProfilingSandbox/README.md @@ -0,0 +1,2 @@ +## ImageSharp.Tests.ProfilingSandbox +Helper project to run and profile unit tests or other "sandbox" code from a single .exe entry point. diff --git a/tests/ImageSharp.Sandbox46/app.config b/tests/ImageSharp.Tests.ProfilingSandbox/app.config similarity index 100% rename from tests/ImageSharp.Sandbox46/app.config rename to tests/ImageSharp.Tests.ProfilingSandbox/app.config diff --git a/tests/ImageSharp.Tests/AssemblyInfo.cs b/tests/ImageSharp.Tests/AssemblyInfo.cs new file mode 100644 index 0000000000..944fbe101e --- /dev/null +++ b/tests/ImageSharp.Tests/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("ImageSharp.Tests.ProfilingSandbox")] diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 6f68d04288..6b35bbb972 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Tests public Configuration ConfigurationEmpty { get; } public Configuration DefaultConfiguration { get; } + private readonly int expectedDefaultConfigurationCount = 5; + public ConfigurationTests() { // the shallow copy of configuration should behave exactly like the default configuration, @@ -61,12 +63,28 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData(0)] - [InlineData(-42)] - public void Set_MaxDegreeOfParallelism_ToNonPositiveValue_Throws(int value) + [InlineData(-3, true)] + [InlineData(-2, true)] + [InlineData(-1, false)] + [InlineData(0, true)] + [InlineData(1, false)] + [InlineData(5, false)] + public void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) { var cfg = new Configuration(); - Assert.Throws(() => cfg.MaxDegreeOfParallelism = value); + if (throws) + { + Assert.Throws( + () => + { + cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism; + }); + } + else + { + cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism; + Assert.Equal(maxDegreeOfParallelism, cfg.MaxDegreeOfParallelism); + } } @@ -92,14 +110,13 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ConfigurationCannotAddDuplicates() { - const int count = 4; Configuration config = this.DefaultConfiguration; - Assert.Equal(count, config.ImageFormats.Count()); + Assert.Equal(expectedDefaultConfigurationCount, config.ImageFormats.Count()); config.ImageFormatsManager.AddImageFormat(BmpFormat.Instance); - Assert.Equal(count, config.ImageFormats.Count()); + Assert.Equal(expectedDefaultConfigurationCount, config.ImageFormats.Count()); } [Fact] @@ -107,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests { Configuration config = Configuration.CreateDefaultInstance(); - Assert.Equal(4, config.ImageFormats.Count()); + Assert.Equal(expectedDefaultConfigurationCount, config.ImageFormats.Count()); } [Fact] @@ -117,4 +134,4 @@ namespace SixLabors.ImageSharp.Tests Assert.True(config.WorkingBufferSizeHintInBytes > 1024); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs b/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs deleted file mode 100644 index de5b2bf474..0000000000 --- a/tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class DrawBezierTests - { - public static readonly TheoryData DrawPathData = new TheoryData - { - { "White", 255, 1.5f }, - { "Red", 255, 3 }, - { "HotPink", 255, 5 }, - { "HotPink", 150, 5 }, - { "White", 255, 15 }, - }; - - [Theory] - [WithSolidFilledImages(nameof(DrawPathData), 300, 450, "Blue", PixelTypes.Rgba32)] - public void DrawBeziers(TestImageProvider provider, string colorName, byte alpha, float thickness) - where TPixel : struct, IPixel - { - var points = new SixLabors.Primitives.PointF[] - { - new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - }; - Rgba32 rgba = TestUtils.GetColorByName(colorName); - rgba.A = alpha; - Color color = rgba; - - FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}"; - - provider.RunValidatingProcessorTest( x => x.DrawBeziers(color, 5f, points), - testDetails, - appendSourceFileOrDescription: false, - appendPixelTypeToFileName: false); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs deleted file mode 100644 index 76d29dff38..0000000000 --- a/tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs +++ /dev/null @@ -1,69 +0,0 @@ -// 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.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class DrawComplexPolygonTests - { - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, false, false)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, true, false, false)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, true, false)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, false, true)] - public void DrawComplexPolygon(TestImageProvider provider, bool overlap, bool transparent, bool dashed) - where TPixel :struct, IPixel - { - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - overlap ? new Vector2(130, 40) : new Vector2(93, 85), - new Vector2(65, 137))); - IPath clipped = simplePath.Clip(hole1); - - Rgba32 colorRgba = Rgba32.White; - if (transparent) - { - colorRgba.A = 150; - } - - Color color = colorRgba; - - string testDetails = ""; - if (overlap) - { - testDetails += "_Overlap"; - } - - if (transparent) - { - testDetails += "_Transparent"; - } - - if (dashed) - { - testDetails += "_Dashed"; - } - - Pen pen = dashed ? Pens.Dash(color, 5f) : Pens.Solid(color, 5f); - - provider.RunValidatingProcessorTest( - x => x.Draw(pen, clipped), - testDetails, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 86c1c28504..d1fb54703f 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System; - +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; @@ -127,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing using (Image background = provider.GetImage()) using (var overlay = new Image(50, 50)) { - overlay.Mutate(c => c.Fill(Rgba32.Black)); + overlay.GetPixelSpan().Fill(Rgba32.Black); background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F)); @@ -190,7 +189,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing void Test() { - background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); + background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions())); } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs deleted file mode 100644 index 2836f8a38d..0000000000 --- a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class DrawLinesTests - { - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 2.5, true)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, 10, true)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, 10, true)] - public void DrawLines_Simple(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) - where TPixel : struct, IPixel - { - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = new Pen(color, thickness); - - DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); - } - - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] - public void DrawLines_Dash(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) - where TPixel : struct, IPixel - { - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.Dash(color, thickness); - - DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); - } - - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "LightGreen", 1f, 5, false)] - public void DrawLines_Dot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) - where TPixel : struct, IPixel - { - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.Dot(color, thickness); - - DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); - } - - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Yellow", 1f, 5, false)] - public void DrawLines_DashDot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) - where TPixel : struct, IPixel - { - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.DashDot(color, thickness); - - DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); - } - - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Black", 1f, 5, false)] - public void DrawLines_DashDotDot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) - where TPixel : struct, IPixel - { - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - Pen pen = Pens.DashDotDot(color, thickness); - - DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); - } - - - private static void DrawLinesImpl( - TestImageProvider provider, - string colorName, - float alpha, - float thickness, - bool antialias, - Pen pen) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - - GraphicsOptions options = new GraphicsOptions(antialias); - - string aa = antialias ? "" : "_NoAntialias"; - FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; - - provider.RunValidatingProcessorTest( - c => c.DrawLines(options, pen, simplePath), - outputDetails, - appendSourceFileOrDescription: false); - } - - } -} diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs deleted file mode 100644 index 8c2c6fc6ed..0000000000 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; -using SixLabors.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class DrawPathTests - { - public static readonly TheoryData DrawPathData = new TheoryData - { - { "White", 255, 1.5f }, - { "Red", 255, 3 }, - { "HotPink", 255, 5 }, - { "HotPink", 150, 5 }, - { "White", 255, 15 }, - }; - - [Theory] - [WithSolidFilledImages(nameof(DrawPathData), 300, 450, "Blue", PixelTypes.Rgba32)] - public void DrawPath(TestImageProvider provider, string colorName, byte alpha, float thickness) - where TPixel : struct, IPixel - { - var linearSegment = new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300)); - var bezierSegment = new CubicBezierLineSegment( - new Vector2(50, 300), - new Vector2(500, 500), - new Vector2(60, 10), - new Vector2(10, 400)); - - var path = new Path(linearSegment, bezierSegment); - - Rgba32 rgba = TestUtils.GetColorByName(colorName); - rgba.A = alpha; - Color color = rgba; - - FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}"; - - provider.RunValidatingProcessorTest( - x => x.Draw(color, thickness, path), - testDetails, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - - [Theory] - [WithSolidFilledImages(256, 256, "Black", PixelTypes.Rgba32)] - public void PathExtendingOffEdgeOfImageShouldNotBeCropped(TestImageProvider provider) - where TPixel : struct, IPixel - { - var color = Color.White; - Pen pen = Pens.Solid(color, 5f); - - provider.RunValidatingProcessorTest( - x => - { - for (int i = 0; i < 300; i += 20) - { - var points = new PointF[] { new Vector2(100, 2), new Vector2(-10, i) }; - x.DrawLines(pen, points); - } - }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs deleted file mode 100644 index 18fde6ad8f..0000000000 --- a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class DrawPolygonTests - { - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 2.5, true)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, 10, true)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, 10, true)] - public void DrawPolygon(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = - { - new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }; - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - - GraphicsOptions options = new GraphicsOptions(antialias); - - string aa = antialias ? "" : "_NoAntialias"; - FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; - - provider.RunValidatingProcessorTest( - c => c.DrawPolygon(options, color, thickness, simplePath), - outputDetails, - appendSourceFileOrDescription: false); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs deleted file mode 100644 index e0fff8da50..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -// 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.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class FillComplexPolygonTests - { - [Theory] - [WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, false, false)] - [WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, true, false)] - [WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, false, true)] - public void ComplexPolygon_SolidFill(TestImageProvider provider, bool overlap, bool transparent) - where TPixel :struct, IPixel - { - var simplePath = new Polygon(new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300))); - - var hole1 = new Polygon(new LinearLineSegment( - new Vector2(37, 85), - overlap ? new Vector2(130, 40) : new Vector2(93, 85), - new Vector2(65, 137))); - IPath clipped = simplePath.Clip(hole1); - - Rgba32 colorRgba = Rgba32.HotPink; - if (transparent) - { - colorRgba.A = 150; - } - - Color color = colorRgba; - - string testDetails = ""; - if (overlap) - { - testDetails += "_Overlap"; - } - - if (transparent) - { - testDetails += "_Transparent"; - } - - provider.RunValidatingProcessorTest( - x => x.Fill(color, clipped), - testDetails, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs b/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs deleted file mode 100644 index c61f770c9f..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs +++ /dev/null @@ -1,147 +0,0 @@ -// 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.Drawing -{ - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - - [GroupOutput("Drawing/GradientBrushes")] - public class FillEllipticGradientBrushTests - { - public static ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.01f); - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void WithEqualColorsReturnsUnicolorImage( - TestImageProvider provider) - where TPixel : struct, IPixel - { - Color red = Color.Red; - - using (Image image = provider.GetImage()) - { - var unicolorLinearGradientBrush = - new EllipticGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(10, 0), - 1.0f, - GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, red)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - - // no need for reference image in this test: - image.ComparePixelBufferTo(red); - } - } - - [Theory] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.1)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.4)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.8)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.0)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.2)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.6)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 2.0)] - public void AxisParallelEllipsesWithDifferentRatio( - TestImageProvider provider, - float ratio) - where TPixel : struct, IPixel - { - Color yellow = Color.Yellow; - Color red = Color.Red; - Color black = Color.Black; - - provider.VerifyOperation( - TolerantComparer, - image => - { - var unicolorLinearGradientBrush = new EllipticGradientBrush( - new SixLabors.Primitives.Point(image.Width / 2, image.Height / 2), - new SixLabors.Primitives.Point(image.Width / 2, (image.Width * 2) / 3), - ratio, - GradientRepetitionMode.None, - new ColorStop(0, yellow), - new ColorStop(1, red), - new ColorStop(1, black)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - $"{ratio:F2}", - false, - false); - } - - [Theory] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.1, 0)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.4, 0)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.8, 0)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.0, 0)] - - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.1, 45)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.4, 45)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.8, 45)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.0, 45)] - - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.1, 90)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.4, 90)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.8, 90)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.0, 90)] - - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.1, 30)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.4, 30)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0.8, 30)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 1.0, 30)] - public void RotatedEllipsesWithDifferentRatio( - TestImageProvider provider, - float ratio, - float rotationInDegree) - where TPixel: struct, IPixel - { - FormattableString variant = $"{ratio:F2}_AT_{rotationInDegree:00}deg"; - - provider.VerifyOperation( - TolerantComparer, - image => - { - Color yellow = Color.Yellow; - Color red = Color.Red; - Color black = Color.Black; - - var center = new SixLabors.Primitives.Point(image.Width / 2, image.Height / 2); - - double rotation = (Math.PI * rotationInDegree) / 180.0; - double cos = Math.Cos(rotation); - double sin = Math.Sin(rotation); - - int offsetY = image.Height / 6; - int axisX = center.X + (int)-(offsetY * sin); - int axisY = center.Y + (int)(offsetY * cos); - - var unicolorLinearGradientBrush = new EllipticGradientBrush( - center, - new SixLabors.Primitives.Point(axisX, axisY), - ratio, - GradientRepetitionMode.None, - new ColorStop(0, yellow), - new ColorStop(1, red), - new ColorStop(1, black)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - variant, - false, - false); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs deleted file mode 100644 index cbf49b8301..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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.Drawing -{ - [GroupOutput("Drawing")] - public class FillImageBrushTests - { - [Fact] - public void DoesNotDisposeImage() - { - using (var src = new Image(5, 5)) - { - var brush = new ImageBrush(src); - using (var dest = new Image(10, 10)) - { - dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); - dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10))); - } - } - } - - [Theory] - [WithTestPatternImages(200, 200, PixelTypes.Rgba32 | PixelTypes.Bgra32)] - public void UseBrushOfDifferentPixelType(TestImageProvider provider) - where TPixel : struct, IPixel - { - byte[] data = TestFile.Create(TestImages.Png.Ducky).Bytes; - using (Image background = provider.GetImage()) - using (Image overlay = provider.PixelType == PixelTypes.Rgba32 - ? (Image)Image.Load(data) - : Image.Load(data)) - { - var brush = new ImageBrush(overlay); - background.Mutate(c => c.Fill(brush)); - - background.DebugSave(provider, appendSourceFileOrDescription : false); - background.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs deleted file mode 100644 index 361e7e70d1..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Globalization; -using System.Linq; -using System.Text; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -using Xunit; -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - using SixLabors.ImageSharp.Advanced; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - - [GroupOutput("Drawing/GradientBrushes")] - public class FillLinearGradientBrushTests - { - public static ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.01f); - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void WithEqualColorsReturnsUnicolorImage(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Color red = Color.Red; - - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(10, 0), - GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, red)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - - // no need for reference image in this test: - image.ComparePixelBufferTo(red); - } - } - - [Theory] - [WithBlankImages(20, 10, PixelTypes.Rgba32)] - [WithBlankImages(20, 10, PixelTypes.Argb32)] - [WithBlankImages(20, 10, PixelTypes.Rgb24)] - public void DoesNotDependOnSinglePixelType(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(image.Width, 0), - GradientRepetitionMode.None, - new ColorStop(0, Color.Blue), - new ColorStop(1, Color.Yellow)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - appendSourceFileOrDescription: false); - } - - [Theory] - [WithBlankImages(500, 10, PixelTypes.Rgba32)] - public void HorizontalReturnsUnicolorColumns(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - Color red = Color.Red; - Color yellow = Color.Yellow; - - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(image.Width, 0), - GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, yellow)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - false, - false); - } - - [Theory] - [WithBlankImages(500, 10, PixelTypes.Rgba32, GradientRepetitionMode.DontFill)] - [WithBlankImages(500, 10, PixelTypes.Rgba32, GradientRepetitionMode.None)] - [WithBlankImages(500, 10, PixelTypes.Rgba32, GradientRepetitionMode.Repeat)] - [WithBlankImages(500, 10, PixelTypes.Rgba32, GradientRepetitionMode.Reflect)] - public void HorizontalGradientWithRepMode( - TestImageProvider provider, - GradientRepetitionMode repetitionMode) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - Color red = Color.Red; - Color yellow = Color.Yellow; - - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(image.Width / 10, 0), - repetitionMode, - new ColorStop(0, red), - new ColorStop(1, yellow)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - $"{repetitionMode}", - false, - false); - } - - [Theory] - [WithBlankImages(200, 100, PixelTypes.Rgba32, new[] { 0.5f })] - [WithBlankImages(200, 100, PixelTypes.Rgba32, new[] { 0.2f, 0.4f, 0.6f, 0.8f })] - [WithBlankImages(200, 100, PixelTypes.Rgba32, new[] { 0.1f, 0.3f, 0.6f })] - public void WithDoubledStopsProduceDashedPatterns( - TestImageProvider provider, - float[] pattern) - where TPixel : struct, IPixel - { - string variant = string.Join("_", pattern.Select(i => i.ToString(CultureInfo.InvariantCulture))); - - // ensure the input data is valid - Assert.True(pattern.Length > 0); - - Color black = Color.Black; - Color white = Color.White; - - // create the input pattern: 0, followed by each of the arguments twice, followed by 1.0 - toggling black and white. - ColorStop[] colorStops = - Enumerable.Repeat(new ColorStop(0, black), 1) - .Concat( - pattern - .SelectMany((f, index) => new[] - { - new ColorStop(f, index % 2 == 0 ? black : white), - new ColorStop(f, index % 2 == 0 ? white : black) - })) - .Concat(Enumerable.Repeat(new ColorStop(1, pattern.Length % 2 == 0 ? black : white), 1)) - .ToArray(); - - using (Image image = provider.GetImage()) - { - var unicolorLinearGradientBrush = - new LinearGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(image.Width, 0), - GradientRepetitionMode.None, - colorStops); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - - image.DebugSave( - provider, - variant, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - // the result must be a black and white pattern, no other color should occur: - Assert.All( - Enumerable.Range(0, image.Width).Select(i => image[i, 0]), - color => Assert.True( - color.Equals(black.ToPixel()) || color.Equals(white.ToPixel()))); - - image.CompareToReferenceOutput( - TolerantComparer, - provider, - variant, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } - - [Theory] - [WithBlankImages(10, 500, PixelTypes.Rgba32)] - public void VerticalBrushReturnsUnicolorRows( - TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - image => - { - Color red = Color.Red; - Color yellow = Color.Yellow; - - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(0, 0), - new SixLabors.Primitives.Point(0, image.Height), - GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, yellow)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - - VerifyAllRowsAreUnicolor(image); - }, - false, - false); - - void VerifyAllRowsAreUnicolor(Image image) - { - for (int y = 0; y < image.Height; y++) - { - Span row = image.GetPixelRowSpan(y); - TPixel firstColorOfRow = row[0]; - foreach (TPixel p in row) - { - Assert.Equal(firstColorOfRow, p); - } - } - } - } - - public enum ImageCorner - { - TopLeft = 0, - TopRight = 1, - BottomLeft = 2, - BottomRight = 3 - } - - [Theory] - [WithBlankImages(200, 200, PixelTypes.Rgba32, ImageCorner.TopLeft)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, ImageCorner.TopRight)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, ImageCorner.BottomLeft)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, ImageCorner.BottomRight)] - public void DiagonalReturnsCorrectImages( - TestImageProvider provider, - ImageCorner startCorner) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Assert.True(image.Height == image.Width, "For the math check block at the end the image must be squared, but it is not."); - - int startX = (int)startCorner % 2 == 0 ? 0 : image.Width - 1; - int startY = startCorner > ImageCorner.TopRight ? 0 : image.Height - 1; - int endX = image.Height - startX - 1; - int endY = image.Width - startY - 1; - - Color red = Color.Red; - Color yellow = Color.Yellow; - - var unicolorLinearGradientBrush = - new LinearGradientBrush( - new SixLabors.Primitives.Point(startX, startY), - new SixLabors.Primitives.Point(endX, endY), - GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, yellow)); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - image.DebugSave( - provider, - startCorner, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - int verticalSign = startY == 0 ? 1 : -1; - int horizontalSign = startX == 0 ? 1 : -1; - - for (int i = 0; i < image.Height; i++) - { - // it's diagonal, so for any (a, a) on the gradient line, for all (a-x, b+x) - +/- depending on the diagonal direction - must be the same color) - TPixel colorOnDiagonal = image[i, i]; - - // TODO: This is incorrect. from -0 to < 0 ?? - int orthoCount = 0; - for (int offset = -orthoCount; offset < orthoCount; offset++) - { - Assert.Equal(colorOnDiagonal, image[i + (horizontalSign * offset), i + (verticalSign * offset)]); - } - } - - image.CompareToReferenceOutput( - TolerantComparer, - provider, - startCorner, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } - - [Theory] - [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 0, 499, 499, new[] { 0f, .2f, .5f, .9f }, new[] { 0, 0, 1, 1 })] - [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 499, 499, 0, new[] { 0f, 0.2f, 0.5f, 0.9f }, new[] { 0, 1, 2, 3 })] - [WithBlankImages(500, 500, PixelTypes.Rgba32, 499, 499, 0, 0, new[] { 0f, 0.7f, 0.8f, 0.9f }, new[] { 0, 1, 2, 0 })] - [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 0, 499, 499, new[] { 0f, .5f, 1f }, new[] { 0, 1, 3 })] - public void ArbitraryGradients( - TestImageProvider provider, - int startX, int startY, - int endX, int endY, - float[] stopPositions, - int[] stopColorCodes) - where TPixel : struct, IPixel - { - Color[] colors = - { - Color.Navy, Color.LightGreen, Color.Yellow, - Color.Red - }; - - var coloringVariant = new StringBuilder(); - var colorStops = new ColorStop[stopPositions.Length]; - - for (int i = 0; i < stopPositions.Length; i++) - { - Color color = colors[stopColorCodes[i % colors.Length]]; - float position = stopPositions[i]; - colorStops[i] = new ColorStop(position, color); - Rgba32 rgba = color; - coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position); - } - - FormattableString variant = $"({startX},{startY})_TO_({endX},{endY})__[{coloringVariant}]"; - - provider.VerifyOperation( - image => - { - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(startX, startY), - new SixLabors.Primitives.Point(endX, endY), - GradientRepetitionMode.None, - colorStops); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - variant, - false, - false); - } - - [Theory] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0, 0, 199, 199, new[] { 0f, .25f, .5f, .75f, 1f }, new[] { 0, 1, 2, 3, 4 })] - public void MultiplePointGradients( - TestImageProvider provider, - int startX, int startY, - int endX, int endY, - float[] stopPositions, - int[] stopColorCodes) - where TPixel : struct, IPixel - { - Color[] colors = - { - Color.Black, Color.Blue, Color.Red, - Color.White, Color.Lime - }; - - var coloringVariant = new StringBuilder(); - var colorStops = new ColorStop[stopPositions.Length]; - - for (int i = 0; i < stopPositions.Length; i++) - { - Color color = colors[stopColorCodes[i % colors.Length]]; - float position = stopPositions[i]; - colorStops[i] = new ColorStop(position, color); - Rgba32 rgba = color; - coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position); - } - - FormattableString variant = $"({startX},{startY})_TO_({endX},{endY})__[{coloringVariant}]"; - - provider.VerifyOperation( - image => - { - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(startX, startY), - new SixLabors.Primitives.Point(endX, endY), - GradientRepetitionMode.None, - colorStops); - - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, - variant, - false, - false); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs deleted file mode 100644 index 1ab747bafc..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs +++ /dev/null @@ -1,157 +0,0 @@ -// 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 SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing/GradientBrushes")] - public class FillPathGradientBrushTests - { - public static ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.01f); - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void FillRectangleWithDifferentColors(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - - var brush = new PathGradientBrush(points, colors); - - image.Mutate(x => x.Fill(brush)); - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - }); - } - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void FillTriangleWithDifferentColors(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - PointF[] points = { new PointF(5, 0), new PointF(10, 10), new PointF(0, 10) }; - Color[] colors = { Color.Red, Color.Green, Color.Blue }; - - var brush = new PathGradientBrush(points, colors); - - image.Mutate(x => x.Fill(brush)); - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - }); - } - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void FillRectangleWithSingleColor(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - Color[] colors = { Color.Red }; - - var brush = new PathGradientBrush(points, colors); - - image.Mutate(x => x.Fill(brush)); - - image.ComparePixelBufferTo(Color.Red); - } - } - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void ShouldRotateTheColorsWhenThereAreMorePoints(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - Color[] colors = { Color.Red, Color.Yellow }; - - var brush = new PathGradientBrush(points, colors); - - image.Mutate(x => x.Fill(brush)); - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - }); - } - - [Theory] - [WithBlankImages(10, 10, PixelTypes.Rgba32)] - public void FillWithCustomCenterColor(TestImageProvider provider) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - - var brush = new PathGradientBrush(points, colors, Color.White); - - image.Mutate(x => x.Fill(brush)); - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - }); - } - - [Fact] - public void ShouldThrowArgumentNullExceptionWhenLinesAreNull() - { - Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - - PathGradientBrush Create() => new PathGradientBrush(null, colors, Color.White); - - Assert.Throws(Create); - } - - [Fact] - public void ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3PointsAreGiven() - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0) }; - Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - - PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); - - Assert.Throws(Create); - } - - [Fact] - public void ShouldThrowArgumentNullExceptionWhenColorsAreNull() - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - - PathGradientBrush Create() => new PathGradientBrush(points, null, Color.White); - - Assert.Throws(Create); - } - - [Fact] - public void ShouldThrowArgumentOutOfRangeExceptionWhenEmptyColorArrayIsGiven() - { - PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - - var colors = new Color[0]; - - PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); - - Assert.Throws(Create); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs deleted file mode 100644 index 647f285103..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - public class FillPatternBrushTests - { - private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) - { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FillPatternBrushTests"); - using (var image = new Image(20, 20)) - { - image.Mutate(x => x.Fill(background).Fill(brush)); - - image.Save($"{path}/{name}.png"); - - Buffer2D sourcePixels = image.GetRootFramePixelBuffer(); - // lets pick random spots to start checking - var r = new Random(); - var expectedPatternFast = new DenseMatrix(expectedPattern); - int xStride = expectedPatternFast.Columns; - int yStride = expectedPatternFast.Rows; - int offsetX = r.Next(image.Width / xStride) * xStride; - int offsetY = r.Next(image.Height / yStride) * yStride; - for (int x = 0; x < xStride; x++) - { - for (int y = 0; y < yStride; y++) - { - int actualX = x + offsetX; - int actualY = y + offsetY; - Rgba32 expected = expectedPatternFast[y, x]; // inverted pattern - Rgba32 actual = sourcePixels[actualX, actualY]; - if (expected != actual) - { - Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})"); - } - } - } - - image.Mutate(x => x.Resize(80, 80, KnownResamplers.NearestNeighbor)); - image.Save($"{path}/{name}x4.png"); - } - } - - [Fact] - public void ImageShouldBeFloodFilledWithPercent10() - { - this.Test( - "Percent10", - Rgba32.Blue, - Brushes.Percent10(Rgba32.HotPink, Rgba32.LimeGreen), - new[,] - { - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithPercent10Transparent() - { - this.Test( - "Percent10_Transparent", - Rgba32.Blue, - Brushes.Percent10(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithPercent20() - { - this.Test( - "Percent20", - Rgba32.Blue, - Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] - { - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithPercent20_transparent() - { - this.Test( - "Percent20_Transparent", - Rgba32.Blue, - Brushes.Percent20(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithHorizontal() - { - this.Test( - "Horizontal", - Rgba32.Blue, - Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] - { - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithHorizontal_transparent() - { - this.Test( - "Horizontal_Transparent", - Rgba32.Blue, - Brushes.Horizontal(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithMin() - { - this.Test( - "Min", - Rgba32.Blue, - Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] - { - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithMin_transparent() - { - this.Test( - "Min_Transparent", - Rgba32.Blue, - Brushes.Min(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }, - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithVertical() - { - this.Test( - "Vertical", - Rgba32.Blue, - Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] - { - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithVertical_transparent() - { - this.Test( - "Vertical_Transparent", - Rgba32.Blue, - Brushes.Vertical(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithForwardDiagonal() - { - this.Test( - "ForwardDiagonal", - Rgba32.Blue, - Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] - { - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithForwardDiagonal_transparent() - { - this.Test( - "ForwardDiagonal_Transparent", - Rgba32.Blue, - Brushes.ForwardDiagonal(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithBackwardDiagonal() - { - this.Test( - "BackwardDiagonal", - Rgba32.Blue, - Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] - { - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink } - }); - } - - [Fact] - public void ImageShouldBeFloodFilledWithBackwardDiagonal_transparent() - { - this.Test( - "BackwardDiagonal_Transparent", - Rgba32.Blue, - Brushes.BackwardDiagonal(Rgba32.HotPink), - new Rgba32[,] - { - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink } - }); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs deleted file mode 100644 index 104237ec3e..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Shapes; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class FillPolygonTests - { - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, true)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, true)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, false)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, true)] - public void FillPolygon_Solid(TestImageProvider provider, string colorName, float alpha, bool antialias) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = - { - new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }; - Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - - var options = new GraphicsOptions(antialias); - - string aa = antialias ? "" : "_NoAntialias"; - FormattableString outputDetails = $"{colorName}_A{alpha}{aa}"; - - provider.RunValidatingProcessorTest( - c => c.FillPolygon(options, color, simplePath), - outputDetails, - appendSourceFileOrDescription: false); - } - - [Theory] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)] - public void FillPolygon_Concave(TestImageProvider provider) - where TPixel : struct, IPixel - { - var points = new SixLabors.Primitives.PointF[] - { - new Vector2(8, 8), - new Vector2(64, 8), - new Vector2(64, 64), - new Vector2(120, 64), - new Vector2(120, 120), - new Vector2(8, 120) - }; - - var color = Color.LightGreen; - - provider.RunValidatingProcessorTest( - c => c.FillPolygon(color, points), - appendSourceFileOrDescription: false, - appendPixelTypeToFileName: false); - } - - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)] - public void FillPolygon_Pattern(TestImageProvider provider) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = - { - new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }; - var color = Color.Yellow; - - var brush = Brushes.Horizontal(color); - - provider.RunValidatingProcessorTest( - c => c.FillPolygon(brush, simplePath), - appendSourceFileOrDescription: false); - } - - [Theory] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Png.Ducky)] - [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Bmp.Car)] - public void FillPolygon_ImageBrush(TestImageProvider provider, string brushImageName) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = - { - new Vector2(10, 10), new Vector2(200, 50), new Vector2(50, 200) - }; - - using (Image brushImage = Image.Load(TestFile.Create(brushImageName).Bytes)) - { - var brush = new ImageBrush(brushImage); - - provider.RunValidatingProcessorTest( - c => c.FillPolygon(brush, simplePath), - System.IO.Path.GetFileNameWithoutExtension(brushImageName), - appendSourceFileOrDescription: false); - } - } - - [Theory] - [WithBasicTestPatternImages(250, 250, PixelTypes.Rgba32)] - public void Fill_RectangularPolygon(TestImageProvider provider) - where TPixel : struct, IPixel - { - var polygon = new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140); - var color = Color.White; - - provider.RunValidatingProcessorTest( - c => c.Fill(color, polygon), - appendSourceFileOrDescription: false); - } - - [Theory] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 50, 0f)] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, 20f)] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, -180f)] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 5, 70, 0f)] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 7, 80, -180f)] - public void Fill_RegularPolygon(TestImageProvider provider, int vertices, float radius, float angleDeg) - where TPixel : struct, IPixel - { - float angle = GeometryUtilities.DegreeToRadian(angleDeg); - var polygon = new RegularPolygon(100, 100, vertices, radius, angle); - var color = Color.Yellow; - - FormattableString testOutput = $"V({vertices})_R({radius})_Ang({angleDeg})"; - provider.RunValidatingProcessorTest( - c => c.Fill(color, polygon), - testOutput, - appendSourceFileOrDescription: false, - appendPixelTypeToFileName: false); - } - - [Theory] - [WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)] - public void Fill_EllipsePolygon(TestImageProvider provider) - where TPixel : struct, IPixel - { - var polygon = new EllipsePolygon(100, 100, 80, 120); - var color = Color.Azure; - - provider.RunValidatingProcessorTest( - c => c.Fill(color, polygon), - appendSourceFileOrDescription: false, - appendPixelTypeToFileName: false); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs deleted file mode 100644 index 818340dd22..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - - [GroupOutput("Drawing/GradientBrushes")] - public class FillRadialGradientBrushTests - { - public static ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.01f); - - [Theory] - [WithBlankImages(200, 200, PixelTypes.Rgba32)] - public void WithEqualColorsReturnsUnicolorImage( - TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Color red = Color.Red; - - var unicolorRadialGradientBrush = - new RadialGradientBrush( - new SixLabors.Primitives.Point(0, 0), - 100, - GradientRepetitionMode.None, - new ColorStop(0, red), - new ColorStop(1, red)); - - image.Mutate(x => x.Fill(unicolorRadialGradientBrush)); - - image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - - // no need for reference image in this test: - image.ComparePixelBufferTo(red); - } - } - - [Theory] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 100, 100)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0, 0)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 100, 0)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, 0, 100)] - [WithBlankImages(200, 200, PixelTypes.Rgba32, -40, 100)] - public void WithDifferentCentersReturnsImage( - TestImageProvider provider, - int centerX, - int centerY) - where TPixel : struct, IPixel - { - provider.VerifyOperation( - TolerantComparer, - image => - { - var brush = new RadialGradientBrush( - new SixLabors.Primitives.Point(centerX, centerY), - image.Width / 2f, - GradientRepetitionMode.None, - new ColorStop(0, Color.Red), - new ColorStop(1, Color.Yellow)); - - image.Mutate(x => x.Fill(brush)); - }, - $"center({centerX},{centerY})", - false, - false); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs deleted file mode 100644 index c0388ea2d4..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -using Moq; -using System; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; -using Xunit; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.Shapes; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - - - public class FillRegionProcessorTests - { - - [Theory] - [InlineData(true, 1, 4)] - [InlineData(true, 2, 4)] - [InlineData(true, 5, 5)] - [InlineData(true, 8, 8)] - [InlineData(false, 8, 4)] - [InlineData(false, 16, 4)] // we always do 4 sub=pixels when antialiasing is off. - public void MinimumAntialiasSubpixelDepth(bool antialias, int antialiasSubpixelDepth, int expectedAntialiasSubpixelDepth) - { - var bounds = new Rectangle(0, 0, 1, 1); - - var brush = new Mock(); - var region = new MockRegion2(bounds); - - var options = new GraphicsOptions(antialias) - { - AntialiasSubpixelDepth = 1 - }; - var processor = new FillRegionProcessor(brush.Object, region, options); - var img = new Image(1, 1); - processor.Execute(img, bounds); - - Assert.Equal(4, region.ScanInvocationCounter); - } - - [Fact] - public void FillOffCanvas() - { - var bounds = new Rectangle(-100, -10, 10, 10); - var brush = new Mock(); - var options = new GraphicsOptions(true); - var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); - var img = new Image(10, 10); - processor.Execute(img, bounds); - } - - [Fact] - public void DrawOffCanvas() - { - - using (var img = new Image(10, 10)) - { - img.Mutate(x => x.DrawLines(new Pen(Rgba32.Black, 10), - new Vector2(-10, 5), - new Vector2(20, 5))); - } - } - - [Fact] - public void DoesNotThrowForIssue928() - { - var rectText = new RectangleF(0, 0, 2000, 2000); - using (Image img = new Image((int)rectText.Width, (int)rectText.Height)) - { - img.Mutate(x => x.Fill(Rgba32.Transparent)); - - img.Mutate(ctx => { - ctx.DrawLines( - Rgba32.Red, - 0.984252f, - new PointF(104.762581f, 1074.99365f), - new PointF(104.758667f, 1075.01721f), - new PointF(104.757675f, 1075.04114f), - new PointF(104.759628f, 1075.065f), - new PointF(104.764488f, 1075.08838f), - new PointF(104.772186f, 1075.111f), - new PointF(104.782608f, 1075.13245f), - new PointF(104.782608f, 1075.13245f) - ); - } - ); - } - } - - [Fact] - public void DoesNotThrowFillingTriangle() - { - using(var image = new Image(28, 28)) - { - var path = new Polygon( - new LinearLineSegment(new PointF(17.11f, 13.99659f), new PointF(14.01433f, 27.06201f)), - new LinearLineSegment(new PointF(14.01433f, 27.06201f), new PointF(13.79267f, 14.00023f)), - new LinearLineSegment(new PointF(13.79267f, 14.00023f), new PointF(17.11f, 13.99659f)) - ); - - image.Mutate(ctx => - { - ctx.Fill(Rgba32.White, path); - }); - } - } - - // Mocking the region throws an error in netcore2.0 - private class MockRegion1 : Region - { - public override Rectangle Bounds => new Rectangle(-100, -10, 10, 10); - - public override int Scan(float y, Span buffer, Configuration configuration) - { - if (y < 5) - { - buffer[0] = -10f; - buffer[1] = 100f; - return 2; - } - return 0; - } - - public override int MaxIntersections => 10; - } - - private class MockRegion2 : Region - { - public MockRegion2(Rectangle bounds) - { - this.Bounds = bounds; - } - - public override int MaxIntersections => 100; - - public override Rectangle Bounds { get; } - - public int ScanInvocationCounter { get; private set; } - - public override int Scan(float y, Span buffer, Configuration configuration) - { - this.ScanInvocationCounter++; - return 0; - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs deleted file mode 100644 index a5e7450839..0000000000 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; -using SixLabors.Shapes; - -using Xunit; - -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class FillSolidBrushTests - { - [Theory] - [WithBlankImages(1, 1, PixelTypes.Rgba32)] - [WithBlankImages(7, 4, PixelTypes.Rgba32)] - [WithBlankImages(16, 7, PixelTypes.Rgba32)] - [WithBlankImages(33, 32, PixelTypes.Rgba32)] - [WithBlankImages(400, 500, PixelTypes.Rgba32)] - public void DoesNotDependOnSize(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var color = Color.HotPink; - image.Mutate(c => c.Fill(color)); - - image.DebugSave(provider, appendPixelTypeToFileName: false); - image.ComparePixelBufferTo(color); - } - } - - [Theory] - [WithBlankImages(16, 16, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector)] - public void DoesNotDependOnSinglePixelType(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var color = Color.HotPink; - image.Mutate(c => c.Fill(color)); - - image.DebugSave(provider, appendSourceFileOrDescription: false); - image.ComparePixelBufferTo(color); - } - } - - [Theory] - [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")] - [WithSolidFilledImages(16, 16, "Yellow", PixelTypes.Rgba32, "Khaki")] - public void WhenColorIsOpaque_OverridePreviousColor( - TestImageProvider provider, - string newColorName) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Color color = TestUtils.GetColorByName(newColorName); - image.Mutate(c => c.Fill(color)); - - image.DebugSave( - provider, - newColorName, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - image.ComparePixelBufferTo(color); - } - } - - [Theory] - [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] - [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] - public void FillRegion(TestImageProvider provider, int x0, int y0, int w, int h) - where TPixel : struct, IPixel - { - FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; - var region = new RectangleF(x0, y0, w, h); - Color color = TestUtils.GetColorByName("Blue"); - - provider.RunValidatingProcessorTest(c => c.Fill(color, region), testDetails, ImageComparer.Exact); - } - - [Theory] - [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] - [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] - public void FillRegion_WorksOnWrappedMemoryImage( - TestImageProvider provider, - int x0, - int y0, - int w, - int h) - where TPixel : struct, IPixel - { - FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; - var region = new RectangleF(x0, y0, w, h); - Color color = TestUtils.GetColorByName("Blue"); - - provider.RunValidatingProcessorTestOnWrappedMemoryImage( - c => c.Fill(color, region), - testDetails, - ImageComparer.Exact, - useReferenceOutputFrom: nameof(this.FillRegion)); - } - - public static readonly TheoryData BlendData = - new TheoryData - { - { false, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, - { false, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, - { false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, - { false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, - { false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, - { false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, - { false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, - { false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, - { false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, - { false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, - { false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, - { false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, - { true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, - { true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, - { true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, - { true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, - { true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, - { true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, - { true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, - { true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, - { true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, - { true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, - { true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, - { true, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, - }; - - [Theory] - [WithSolidFilledImages(nameof(BlendData), 16, 16, "Red", PixelTypes.Rgba32)] - public void BlendFillColorOverBackground( - TestImageProvider provider, - bool triggerFillRegion, - string newColorName, - float alpha, - PixelColorBlendingMode blenderMode, - float blendPercentage) - where TPixel : struct, IPixel - { - Color fillColor = TestUtils.GetColorByName(newColorName).WithAlpha(alpha); - - using (Image image = provider.GetImage()) - { - TPixel bgColor = image[0, 0]; - - var options = new GraphicsOptions(false) - { - ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage - }; - - if (triggerFillRegion) - { - var region = new ShapeRegion(new RectangularPolygon(0, 0, 16, 16)); - - image.Mutate(c => c.Fill(options, new SolidBrush(fillColor), region)); - } - else - { - image.Mutate(c => c.Fill(options, new SolidBrush(fillColor))); - } - - var testOutputDetails = new - { - triggerFillRegion = triggerFillRegion, - newColorName = newColorName, - alpha = alpha, - blenderMode = blenderMode, - blendPercentage = blendPercentage - }; - - image.DebugSave( - provider, - testOutputDetails, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - PixelBlender blender = PixelOperations.Instance.GetPixelBlender( - blenderMode, - PixelAlphaCompositionMode.SrcOver); - TPixel expectedPixel = blender.Blend(bgColor, fillColor.ToPixel(), blendPercentage); - - image.ComparePixelBufferTo(expectedPixel); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs deleted file mode 100644 index 3691b54ce3..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.ImageSharp.Tests.Processing; -using SixLabors.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class DrawPathCollection : BaseImageOperationsExtensionTest - { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - Pen pen = Pens.Solid(Rgba32.HotPink, 1); - IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - IPath path2 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - - IPathCollection pathCollection; - - public DrawPathCollection() - { - this.pathCollection = new PathCollection(this.path1, this.path2); - } - - [Fact] - public void CorrectlySetsBrushAndPath() - { - this.operations.Draw(this.pen, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath region = Assert.IsType(processor.Region); - - // path is converted to a polygon before filling - Assert.IsType(region.Shape); - - Assert.Equal(this.pen.StrokeFill, processor.Brush); - } - } - - [Fact] - public void CorrectlySetsBrushPathOptions() - { - this.operations.Draw(this.noneDefault, this.pen, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapePath region = Assert.IsType(processor.Region); - Assert.IsType(region.Shape); - - Assert.Equal(this.pen.StrokeFill, processor.Brush); - } - } - - [Fact] - public void CorrectlySetsColorAndPath() - { - this.operations.Draw(this.color, 1, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath region = Assert.IsType(processor.Region); - Assert.IsType(region.Shape); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } - - [Fact] - public void CorrectlySetsColorPathAndOptions() - { - this.operations.Draw(this.noneDefault, this.color, 1, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapePath region = Assert.IsType(processor.Region); - Assert.IsType(region.Shape); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs deleted file mode 100644 index 160ff22a3e..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.ImageSharp.Tests.Processing; -using SixLabors.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class FillPath : BaseImageOperationsExtensionTest - { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); - IPath path = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - - [Fact] - public void CorrectlySetsBrushAndPath() - { - this.operations.Fill(this.brush, this.path); - var processor = this.Verify(); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - - // path is converted to a polygon before filling - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - Assert.Equal(this.brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsBrushPathOptions() - { - this.operations.Fill(this.noneDefault, this.brush, this.path); - var processor = this.Verify(); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - Assert.Equal(this.brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsColorAndPath() - { - this.operations.Fill(this.color, this.path); - var processor = this.Verify(); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorPathAndOptions() - { - this.operations.Fill(this.noneDefault, this.color, this.path); - var processor = this.Verify(); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs deleted file mode 100644 index b76ee8ffcd..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.ImageSharp.Tests.Processing; -using SixLabors.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class FillPathCollection : BaseImageOperationsExtensionTest - { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); - IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - IPath path2 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - - IPathCollection pathCollection; - - public FillPathCollection() - { - this.pathCollection = new PathCollection(this.path1, this.path2); - } - - [Fact] - public void CorrectlySetsBrushAndPath() - { - this.operations.Fill(this.brush, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - - // path is converted to a polygon before filling - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - Assert.Equal(this.brush, processor.Brush); - } - } - - [Fact] - public void CorrectlySetsBrushPathOptions() - { - this.operations.Fill(this.noneDefault, this.brush, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - Assert.Equal(this.brush, processor.Brush); - } - } - - [Fact] - public void CorrectlySetsColorAndPath() - { - this.operations.Fill(this.color, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } - - [Fact] - public void CorrectlySetsColorPathAndOptions() - { - this.operations.Fill(this.noneDefault, this.color, this.pathCollection); - - for (int i = 0; i < 2; i++) - { - FillRegionProcessor processor = this.Verify(i); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs deleted file mode 100644 index c62a871481..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.ImageSharp.Tests.Processing; -using SixLabors.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class FillPolygon : BaseImageOperationsExtensionTest - { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); - SixLabors.Primitives.PointF[] path = { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - }; - - - [Fact] - public void CorrectlySetsBrushAndPath() - { - this.operations.FillPolygon(this.brush, this.path); - - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - Assert.Equal(this.brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsBrushPathAndOptions() - { - this.operations.FillPolygon(this.noneDefault, this.brush, this.path); - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - Assert.Equal(this.brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsColorAndPath() - { - this.operations.FillPolygon(this.color, this.path); - FillRegionProcessor processor = this.Verify(); - - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorPathAndOptions() - { - this.operations.FillPolygon(this.noneDefault, this.color, this.path); - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Polygon polygon = Assert.IsType(region.Shape); - Assert.IsType(polygon.LineSegments[0]); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs deleted file mode 100644 index 17a2b87c0e..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Drawing; -using SixLabors.ImageSharp.Tests.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class FillRectangle : BaseImageOperationsExtensionTest - { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); - SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); - - [Fact] - public void CorrectlySetsBrushAndRectangle() - { - this.operations.Fill(this.brush, this.rectangle); - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); - Assert.Equal(rect.Location.X, this.rectangle.X); - Assert.Equal(rect.Location.Y, this.rectangle.Y); - Assert.Equal(rect.Size.Width, this.rectangle.Width); - Assert.Equal(rect.Size.Height, this.rectangle.Height); - - Assert.Equal(this.brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsBrushRectangleAndOptions() - { - this.operations.Fill(this.noneDefault, this.brush, this.rectangle); - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); - Assert.Equal(rect.Location.X, this.rectangle.X); - Assert.Equal(rect.Location.Y, this.rectangle.Y); - Assert.Equal(rect.Size.Width, this.rectangle.Width); - Assert.Equal(rect.Size.Height, this.rectangle.Height); - - Assert.Equal(this.brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsColorAndRectangle() - { - this.operations.Fill(this.color, this.rectangle); - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); - Assert.Equal(rect.Location.X, this.rectangle.X); - Assert.Equal(rect.Location.Y, this.rectangle.Y); - Assert.Equal(rect.Size.Width, this.rectangle.Width); - Assert.Equal(rect.Size.Height, this.rectangle.Height); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorRectangleAndOptions() - { - this.operations.Fill(this.noneDefault, this.color, this.rectangle); - FillRegionProcessor processor = this.Verify(); - - Assert.Equal(this.noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); - Assert.Equal(rect.Location.X, this.rectangle.X); - Assert.Equal(rect.Location.Y, this.rectangle.Y); - Assert.Equal(rect.Size.Width, this.rectangle.Width); - Assert.Equal(rect.Size.Height, this.rectangle.Height); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(this.color, brush.Color); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs deleted file mode 100644 index b474f6e47c..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class ShapePathTests - { - // TODO read these back in - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs deleted file mode 100644 index 69dff72369..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Primitives; -using System; -using System.Collections.Generic; -using System.Numerics; -using Moq; -using SixLabors.Primitives; -using SixLabors.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Paths -{ - public class ShapeRegionTests - { - public abstract class MockPath : IPath - { - public abstract RectangleF Bounds { get; } - public IPath AsClosedPath() => this; - - public abstract SegmentInfo PointAlongPath(float distanceAlongPath); - public abstract PointInfo Distance(PointF point); - public abstract IEnumerable Flatten(); - public abstract bool Contains(PointF point); - public abstract IPath Transform(Matrix3x2 matrix); - public abstract PathTypes PathType { get; } - public abstract int MaxIntersections { get; } - public abstract float Length { get; } - - public int FindIntersections(PointF start, PointF end, PointF[] buffer, int offset) - { - return this.FindIntersections(start, end, buffer, 0); - } - - public int FindIntersections(PointF s, PointF e, Span buffer) - { - Assert.Equal(this.TestYToScan, s.Y); - Assert.Equal(this.TestYToScan, e.Y); - Assert.True(s.X < this.Bounds.Left); - Assert.True(e.X > this.Bounds.Right); - - this.TestFindIntersectionsInvocationCounter++; - - return this.TestFindIntersectionsResult; - } - - public int TestFindIntersectionsInvocationCounter { get; private set; } - public virtual int TestYToScan => 10; - public virtual int TestFindIntersectionsResult => 3; - } - - private readonly Mock pathMock; - - private readonly RectangleF bounds; - - public ShapeRegionTests() - { - this.pathMock = new Mock { CallBase = true }; - - this.bounds = new RectangleF(10.5f, 10, 10, 10); - this.pathMock.Setup(x => x.Bounds).Returns(this.bounds); - } - - [Fact] - public void ShapeRegionWithPathRetainsShape() - { - var region = new ShapeRegion(this.pathMock.Object); - - Assert.Equal(this.pathMock.Object, region.Shape); - } - - [Fact] - public void ShapeRegionFromPathConvertsBoundsProxyToShape() - { - var region = new ShapeRegion(this.pathMock.Object); - - Assert.Equal(Math.Floor(this.bounds.Left), region.Bounds.Left); - Assert.Equal(Math.Ceiling(this.bounds.Right), region.Bounds.Right); - - this.pathMock.Verify(x => x.Bounds); - } - - [Fact] - public void ShapeRegionFromPathMaxIntersectionsProxyToShape() - { - var region = new ShapeRegion(this.pathMock.Object); - - int i = region.MaxIntersections; - this.pathMock.Verify(x => x.MaxIntersections); - } - - [Fact] - public void ShapeRegionFromPathScanYProxyToShape() - { - MockPath path = this.pathMock.Object; - int yToScan = path.TestYToScan; - var region = new ShapeRegion(path); - - int i = region.Scan(yToScan, new float[path.TestFindIntersectionsResult], Configuration.Default); - - Assert.Equal(path.TestFindIntersectionsResult, i); - Assert.Equal(1, path.TestFindIntersectionsInvocationCounter); - } - - - [Fact] - public void ShapeRegionFromShapeConvertsBoundsProxyToShape() - { - var region = new ShapeRegion(this.pathMock.Object); - - Assert.Equal(Math.Floor(this.bounds.Left), region.Bounds.Left); - Assert.Equal(Math.Ceiling(this.bounds.Right), region.Bounds.Right); - - this.pathMock.Verify(x => x.Bounds); - } - - [Fact] - public void ShapeRegionFromShapeMaxIntersectionsProxyToShape() - { - var region = new ShapeRegion(this.pathMock.Object); - - int i = region.MaxIntersections; - this.pathMock.Verify(x => x.MaxIntersections); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs deleted file mode 100644 index fae0bf72b4..0000000000 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs +++ /dev/null @@ -1,52 +0,0 @@ -// 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 SixLabors.Primitives; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class RecolorImageTests - { - [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, "Yellow", "Pink", 0.2f)] - [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgra32, "Yellow", "Pink", 0.5f)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.2f)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.6f)] - public void Recolor(TestImageProvider provider, string sourceColorName, string targetColorName, float threshold) - where TPixel : struct, IPixel - { - Color sourceColor = TestUtils.GetColorByName(sourceColorName); - Color targetColor = TestUtils.GetColorByName(targetColorName); - var brush = new RecolorBrush(sourceColor, targetColor, threshold); - - FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}"; - provider.RunValidatingProcessorTest(x => x.Fill(brush), testInfo); - } - - [Theory] - [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgra32, "Yellow", "Pink", 0.5f)] - [WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.2f)] - public void Recolor_InBox(TestImageProvider provider, string sourceColorName, string targetColorName, float threshold) - where TPixel : struct, IPixel - { - Color sourceColor = TestUtils.GetColorByName(sourceColorName); - Color targetColor = TestUtils.GetColorByName(targetColorName); - var brush = new RecolorBrush(sourceColor, targetColor, threshold); - - FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}"; - provider.RunValidatingProcessorTest(x => - { - Size size = x.GetCurrentSize(); - var rectangle = new Rectangle(0, size.Height / 2 - size.Height / 4, size.Width, size.Height / 2); - x.Fill(brush, rectangle); - }, testInfo); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs deleted file mode 100644 index fd8713cccd..0000000000 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -// 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.Shapes; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class SolidBezierTests - { - [Theory] - [WithBlankImages(500, 500, PixelTypes.Rgba32)] - public void FilledBezier(TestImageProvider provider) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = { - new Vector2(10, 400), - new Vector2(30, 10), - new Vector2(240, 30), - new Vector2(300, 400) - }; - - Color blue = Color.Blue; - Color hotPink = Color.HotPink; - - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.BackgroundColor(blue)); - image.Mutate(x => x.Fill(hotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); - image.DebugSave(provider); - image.CompareToReferenceOutput(provider); - } - } - - - [Theory] - [WithBlankImages(500, 500, PixelTypes.Rgba32)] - public void OverlayByFilledPolygonOpacity(TestImageProvider provider) - where TPixel : struct, IPixel - { - SixLabors.Primitives.PointF[] simplePath = { - new Vector2(10, 400), - new Vector2(30, 10), - new Vector2(240, 30), - new Vector2(300, 400) - }; - - var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - - using (var image = provider.GetImage() as Image) - { - image.Mutate(x => x.BackgroundColor(Rgba32.Blue)); - - image.Mutate(x => x.Fill(color, new Polygon(new CubicBezierLineSegment(simplePath)))); - image.DebugSave(provider); - image.CompareToReferenceOutput(provider); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs deleted file mode 100644 index da7c865b96..0000000000 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; -using Xunit; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Drawing -{ - [GroupOutput("Drawing")] - public class SolidFillBlendedShapesTests - { - public static IEnumerable modes = GetAllModeCombinations(); - - private static IEnumerable GetAllModeCombinations() - { - foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) - { - foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) - { - yield return new object[] { blending, composition }; - } - } - } - - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendHotPinkRect( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - int scaleX = img.Width / 100; - int scaleY = img.Height / 100; - img.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) - ) - .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, - Color.HotPink, - new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) - ); - - VerifyImage(provider, blending, composition, img); - } - } - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - int scaleX = img.Width / 100; - int scaleY = img.Height / 100; - img.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - Color.HotPink, - new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - Color.Transparent, - new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) - ); - - VerifyImage(provider, blending, composition, img); - } - } - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - int scaleX = (img.Width / 100); - int scaleY = (img.Height / 100); - img.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - Color.HotPink, - new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); - - var transparentRed = Color.Red.WithAlpha(0.5f); - - img.Mutate( - x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, - transparentRed, - new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) - ); - - VerifyImage(provider, blending, composition, img); ; - } - } - - [Theory] - [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendBlackEllipse( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition) - where TPixel : struct, IPixel - { - using(Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) - { - int scaleX = (dstImg.Width / 100); - int scaleY = (dstImg.Height / 100); - - dstImg.Mutate( - x => x.Fill( - Color.DarkBlue, - new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); - - srcImg.Mutate( - x => x.Fill( - Color.Black, - new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); - - dstImg.Mutate( - x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) - ); - - VerifyImage(provider, blending, composition, dstImg); - } - } - - private static void VerifyImage( - TestImageProvider provider, - PixelColorBlendingMode blending, - PixelAlphaCompositionMode composition, - Image img) - where TPixel : struct, IPixel - { - img.DebugSave( - provider, - new { composition, blending }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - - var comparer = ImageComparer.TolerantPercentage(0.01f, 3); - img.CompareFirstFrameToReferenceOutput(comparer, - provider, - new { composition, blending }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs deleted file mode 100644 index e6866c6579..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.Fonts; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Text; -using SixLabors.ImageSharp.Tests.Processing; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Text -{ - public class DrawText : BaseImageOperationsExtensionTest - { - private readonly FontCollection FontCollection; - - private readonly Font Font; - - public DrawText() - { - this.FontCollection = new FontCollection(); - this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")).CreateFont(12); - } - - [Fact] - public void FillsForEachACharacterWhenBrushSetAndNotPen() - { - this.operations.DrawText( - new TextGraphicsOptions(true), - "123", - this.Font, - Brushes.Solid(Color.Red), - null, - Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void FillsForEachACharacterWhenBrushSetAndNotPenDefaultOptions() - { - this.operations.DrawText("123", this.Font, Brushes.Solid(Color.Red), null, Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void FillsForEachACharacterWhenBrushSet() - { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void FillsForEachACharacterWhenBrushSetDefaultOptions() - { - this.operations.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void FillsForEachACharacterWhenColorSet() - { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Color.Red, Vector2.Zero); - - var processor = this.Verify(0); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(Color.Red, brush.Color); - } - - [Fact] - public void FillsForEachACharacterWhenColorSetDefaultOptions() - { - this.operations.DrawText("123", this.Font, Color.Red, Vector2.Zero); - - var processor = this.Verify(0); - - SolidBrush brush = Assert.IsType(processor.Brush); - Assert.Equal(Color.Red, brush.Color); - } - - [Fact] - public void DrawForEachACharacterWhenPenSetAndNotBrush() - { - this.operations.DrawText( - new TextGraphicsOptions(true), - "123", - this.Font, - null, - Pens.Dash(Color.Red, 1), - Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void DrawForEachACharacterWhenPenSetAndNotBrushDefaultOptions() - { - this.operations.DrawText("123", this.Font, null, Pens.Dash(Color.Red, 1), Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void DrawForEachACharacterWhenPenSet() - { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); - - this.Verify(0); - } - - [Fact] - public void DrawForEachACharacterWhenPenSetDefaultOptions() - { - this.operations.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); - - var processor = this.Verify(0); - - Assert.Equal("123", processor.Text); - Assert.Equal(this.Font, processor.Font); - var penBrush = Assert.IsType(processor.Pen.StrokeFill); - Assert.Equal(Color.Red, penBrush.Color); - Assert.Equal(1, processor.Pen.StrokeWidth); - Assert.Equal(PointF.Empty, processor.Location); - } - - [Fact] - public void DrawForEachACharacterWhenPenSetAndFillFroEachWhenBrushSet() - { - this.operations.DrawText( - new TextGraphicsOptions(true), - "123", - this.Font, - Brushes.Solid(Color.Red), - Pens.Dash(Color.Red, 1), - Vector2.Zero); - - var processor = this.Verify(0); - - Assert.Equal("123", processor.Text); - Assert.Equal(this.Font, processor.Font); - var brush = Assert.IsType(processor.Brush); - Assert.Equal(Color.Red, brush.Color); - Assert.Equal(PointF.Empty, processor.Location); - var penBrush = Assert.IsType(processor.Pen.StrokeFill); - Assert.Equal(Color.Red, penBrush.Color); - Assert.Equal(1, processor.Pen.StrokeWidth); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs deleted file mode 100644 index a767a686ed..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Linq; -using System.Text; -using SixLabors.Fonts; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; - -using Xunit; -using Xunit.Abstractions; - -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests.Drawing.Text -{ - [GroupOutput("Drawing/Text")] - public class DrawTextOnImageTests - { - private const string AB = "AB\nAB"; - - private const string TestText = "Sphinx of black quartz, judge my vow\n0123456789"; - - public static ImageComparer TextDrawingComparer = ImageComparer.TolerantPercentage(1e-5f); - public static ImageComparer OutlinedTextDrawingComparer = ImageComparer.TolerantPercentage(5e-4f); - - public DrawTextOnImageTests(ITestOutputHelper output) - { - this.Output = output; - } - - private ITestOutputHelper Output { get; } - - [Theory] - [WithSolidFilledImages(276, 336, "White", PixelTypes.Rgba32)] - public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688(TestImageProvider provider) - where TPixel : struct, IPixel - { - Font font = CreateFont("OpenSans-Regular.ttf", 36); - var color = Color.Black; - var text = "A short piece of text"; - - using (var img = provider.GetImage()) - { - // measure the text size - SizeF size = TextMeasurer.Measure(text, new RendererOptions(font)); - - //find out how much we need to scale the text to fill the space (up or down) - float scalingFactor = Math.Min(img.Width / size.Width, img.Height / size.Height); - - //create a new font - var scaledFont = new Font(font, scalingFactor * font.Size); - - var center = new PointF(img.Width / 2, img.Height / 2); - var textGraphicOptions = new TextGraphicsOptions(true) - { - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center - }; - - img.Mutate(i => i.DrawText(textGraphicOptions, text, scaledFont, color, center)); - } - } - - [Theory] - [WithSolidFilledImages(1500, 500, "White", PixelTypes.Rgba32)] - public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688_2(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image img = provider.GetImage()) - { - Font font = CreateFont("OpenSans-Regular.ttf", 39); - string text = new string('a', 10000); // exception - // string text = "Hello"; // no exception - Rgba32 color = Rgba32.Black; - var point = new PointF(100, 100); - - img.Mutate(ctx => ctx.DrawText(text, font, color, point)); - } - } - - - [Theory] - [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] - [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] - [WithSolidFilledImages(400, 40, "White", PixelTypes.Rgba32, 20, 0, 0, "OpenSans-Regular.ttf", TestText)] - [WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)] - public void FontShapesAreRenderedCorrectly( - TestImageProvider provider, - int fontSize, - int x, - int y, - string fontName, - string text) - where TPixel : struct, IPixel - { - Font font = CreateFont(fontName, fontSize); - var color = Color.Black; - - provider.VerifyOperation( - TextDrawingComparer, - img => - { - img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y))); - }, - $"{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})", - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: true); - } - - /// - /// Based on: - /// https://github.com/SixLabors/ImageSharp/issues/572 - /// - [Theory] - [WithSolidFilledImages(2480, 3508, "White", PixelTypes.Rgba32)] - public void FontShapesAreRenderedCorrectly_LargeText( - TestImageProvider provider) - where TPixel : struct, IPixel - { - Font font = CreateFont("OpenSans-Regular.ttf", 36); - - var sb = new StringBuilder(); - string str = Repeat(" ", 78) + "THISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDSTHISISTESTWORDS"; - sb.Append(str); - - string newLines = Repeat(Environment.NewLine, 80); - sb.Append(newLines); - - for (int i = 0; i < 10; i++) - { - sb.AppendLine(str); - } - - var textOptions = new TextGraphicsOptions - { - Antialias = true, - ApplyKerning = true, - VerticalAlignment = VerticalAlignment.Top, - HorizontalAlignment = HorizontalAlignment.Left, - }; - - var color = Color.Black; - - // Based on the reported 0.0270% difference with AccuracyMultiple = 8 - // We should avoid quality regressions leading to higher difference! - var comparer = ImageComparer.TolerantPercentage(0.03f); - - provider.VerifyOperation( - comparer, - img => - { - img.Mutate(c => c.DrawText(textOptions, sb.ToString(), font, color, new PointF(10, 5))); - }, - false, - false); - } - - [Theory] - [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] - [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] - [WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)] - public void FontShapesAreRenderedCorrectlyWithAPen( - TestImageProvider provider, - int fontSize, - int x, - int y, - string fontName, - string text) - where TPixel : struct, IPixel - { - Font font = CreateFont(fontName, fontSize); - var color = Color.Black; - - provider.VerifyOperation( - OutlinedTextDrawingComparer, - img => - { - img.Mutate(c => c.DrawText(text, new Font(font, fontSize), null, Pens.Solid(color, 1), new PointF(x, y))); - }, - $"pen_{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})", - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: true); - } - - [Theory] - [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] - [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] - [WithSolidFilledImages(1100, 200, "White", PixelTypes.Rgba32, 50, 150, 100, "OpenSans-Regular.ttf", TestText)] - public void FontShapesAreRenderedCorrectlyWithAPenPatterned( - TestImageProvider provider, - int fontSize, - int x, - int y, - string fontName, - string text) - where TPixel : struct, IPixel - { - Font font = CreateFont(fontName, fontSize); - var color = Color.Black; - - provider.VerifyOperation( - OutlinedTextDrawingComparer, - img => - { - img.Mutate(c => c.DrawText(text, new Font(font, fontSize), null, Pens.DashDot(color, 3), new PointF(x, y))); - }, - $"pen_{fontName}-{fontSize}-{ToTestOutputDisplayText(text)}-({x},{y})", - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: true); - } - - [Theory] - [WithSolidFilledImages(1000, 1500, "White", PixelTypes.Rgba32, "OpenSans-Regular.ttf")] - public void TextPositioningIsRobust(TestImageProvider provider, string fontName) - where TPixel : struct, IPixel - { - Font font = CreateFont(fontName, 30); - - string text = Repeat("Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\n", - 20); - var textOptions = new TextGraphicsOptions(true) { WrapTextWidth = 1000 }; - - string details = fontName.Replace(" ", ""); - - // Based on the reported 0.1755% difference with AccuracyMultiple = 8 - // We should avoid quality regressions leading to higher difference! - var comparer = ImageComparer.TolerantPercentage(0.2f); - - provider.RunValidatingProcessorTest( - x => x.DrawText(textOptions, text, font, Color.Black, new PointF(10, 50)), - details, - comparer, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - - private static string Repeat(string str, int times) => string.Concat(Enumerable.Repeat(str, times)); - - private static string ToTestOutputDisplayText(string text) - { - string fnDisplayText = text.Replace("\n", ""); - fnDisplayText = fnDisplayText.Substring(0, Math.Min(fnDisplayText.Length, 4)); - return fnDisplayText; - } - - private static Font CreateFont(string fontName, int size) - { - var fontCollection = new FontCollection(); - string fontPath = TestFontUtilities.GetPath(fontName); - Font font = fontCollection.Install(fontPath).CreateFont(size); - return font; - } - - - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs deleted file mode 100644 index 0885611c67..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Processing; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Drawing.Text -{ - public class TextGraphicsOptionsTests - { - [Fact] - public void ExplicitCastOfGraphicsOptions() - { - var opt = new GraphicsOptions(false) - { - AntialiasSubpixelDepth = 99 - }; - - TextGraphicsOptions textOptions = opt; - - Assert.False(textOptions.Antialias); - Assert.Equal(99, textOptions.AntialiasSubpixelDepth); - } - - [Fact] - public void ImplicitCastToGraphicsOptions() - { - var textOptions = new TextGraphicsOptions(false) - { - AntialiasSubpixelDepth = 99 - }; - - var opt = (GraphicsOptions)textOptions; - - Assert.False(opt.Antialias); - Assert.Equal(99, opt.AntialiasSubpixelDepth); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/Utils/QuickSortTests.cs b/tests/ImageSharp.Tests/Drawing/Utils/QuickSortTests.cs deleted file mode 100644 index 5ad7a1248d..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Utils/QuickSortTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Tests.Drawing.Utils -{ - using System; - using System.Linq; - - using SixLabors.ImageSharp.Utils; - - using Xunit; - - public class QuickSortTests - { - public static readonly TheoryData Data = new TheoryData - { - new float[]{ 3, 2, 1 }, - new float[0], - new float[] { 42}, - new float[] { 1, 2}, - new float[] { 2, 1}, - new float[] { 5, 1, 2, 3, 0} - }; - - [Theory] - [MemberData(nameof(Data))] - public void Sort(float[] data) - { - float[] expected = data.ToArray(); - - Array.Sort(expected); - - QuickSort.Sort(data); - - Assert.Equal(expected, data); - } - - [Fact] - public void SortSlice() - { - float[] data = { 3, 2, 1, 0, -1 }; - - Span slice = data.AsSpan(1, 3); - QuickSort.Sort(slice); - float[] actual = slice.ToArray(); - float[] expected = { 0, 1, 2 }; - - Assert.Equal(actual, expected); - } - } -} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 7412f70a11..fd9f50a294 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp memStream.Position = 0; using (var output = Image.Load(memStream)) { - BmpMetadata meta = output.Metadata.GetFormatMetadata(BmpFormat.Instance); + BmpMetadata meta = output.Metadata.GetBmpMetadata(); Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); } @@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true); [Theory] - [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.L8, BmpBitsPerPixel.Pixel8)] public void Encode_8BitGray_WithV3Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore( @@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp supportTransparency: false); [Theory] - [WithFile(Bit8Gs, PixelTypes.Gray8, BmpBitsPerPixel.Pixel8)] + [WithFile(Bit8Gs, PixelTypes.L8, BmpBitsPerPixel.Pixel8)] public void Encode_8BitGray_WithV4Header_Works(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel => TestBmpEncoderCore( diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs index 25cf29406e..ccb57c35f1 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using SixLabors.ImageSharp.Formats.Bmp; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs similarity index 86% rename from tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs rename to tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index 7045d64508..4eac337307 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { using static TestImages.Bmp; - public class BmpMetaDataTests + public class BmpMetadataTests { [Fact] public void CloneIsDeep() @@ -41,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp { IImageInfo imageInfo = Image.Identify(stream); Assert.NotNull(imageInfo); - BmpMetadata bitmapMetaData = imageInfo.Metadata.GetFormatMetadata(BmpFormat.Instance); - Assert.NotNull(bitmapMetaData); - Assert.Equal(expectedInfoHeaderType, bitmapMetaData.InfoHeaderType); + BmpMetadata bitmapMetadata = imageInfo.Metadata.GetBmpMetadata(); + Assert.NotNull(bitmapMetadata); + Assert.Equal(expectedInfoHeaderType, bitmapMetadata.InfoHeaderType); } } } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 62e9acf747..ba3587a7a3 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -3,9 +3,6 @@ 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; @@ -13,10 +10,11 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { using System; + using System.Linq; using System.Reflection; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; - using SixLabors.Memory; + using SixLabors.ImageSharp.Memory; public class GeneralFormatTests : FileTestBase { @@ -167,38 +165,49 @@ namespace SixLabors.ImageSharp.Tests [InlineData(100, 100, "jpg")] [InlineData(100, 10, "jpg")] [InlineData(10, 100, "jpg")] - public void CanIdentifyImageLoadedFromBytes(int width, int height, string format) + [InlineData(100, 100, "tga")] + [InlineData(100, 10, "tga")] + [InlineData(10, 100, "tga")] + public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension) { using (var image = Image.LoadPixelData(new Rgba32[width * height], width, height)) { using (var memoryStream = new MemoryStream()) { - image.Save(memoryStream, GetEncoder(format)); + IImageFormat format = GetFormat(extension); + image.Save(memoryStream, format); memoryStream.Position = 0; IImageInfo imageInfo = Image.Identify(memoryStream); Assert.Equal(imageInfo.Width, width); Assert.Equal(imageInfo.Height, height); + memoryStream.Position = 0; + + imageInfo = Image.Identify(memoryStream, out IImageFormat detectedFormat); + + Assert.Equal(format, detectedFormat); } } } - private static IImageEncoder GetEncoder(string format) + [Fact] + public void IdentifyReturnsNullWithInvalidStream() { - switch (format) + byte[] invalid = new byte[10]; + + using (var memoryStream = new MemoryStream(invalid)) { - 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); + IImageInfo imageInfo = Image.Identify(memoryStream, out IImageFormat format); + + Assert.Null(imageInfo); + Assert.Null(format); } } + + private static IImageFormat GetFormat(string format) + { + return Configuration.Default.ImageFormats.FirstOrDefault(x => x.FileExtensions.Contains(format)); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index d6d58eae7a..eb39c28479 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif memStream.Position = 0; using (var output = Image.Load(memStream)) { - GifMetadata metadata = output.Metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata metadata = output.Metadata.GetGifMetadata(); Assert.Equal(1, metadata.Comments.Count); Assert.Equal("ImageSharp", metadata.Comments[0]); } @@ -135,13 +135,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif inStream.Position = 0; var image = Image.Load(inStream); - GifMetadata metaData = image.Metadata.GetFormatMetadata(GifFormat.Instance); - GifFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata metaData = image.Metadata.GetGifMetadata(); + GifFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetGifMetadata(); GifColorTableMode colorMode = metaData.ColorTableMode; var encoder = new GifEncoder { ColorTableMode = colorMode, - Quantizer = new OctreeQuantizer(frameMetaData.ColorTableLength) + Quantizer = new OctreeQuantizer(frameMetadata.ColorTableLength) }; image.Save(outStream, encoder); @@ -150,16 +150,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif outStream.Position = 0; var clone = Image.Load(outStream); - GifMetadata cloneMetaData = clone.Metadata.GetFormatMetadata(GifFormat.Instance); - Assert.Equal(metaData.ColorTableMode, cloneMetaData.ColorTableMode); + GifMetadata cloneMetadata = clone.Metadata.GetGifMetadata(); + Assert.Equal(metaData.ColorTableMode, cloneMetadata.ColorTableMode); // Gifiddle and Cyotek GifInfo say this image has 64 colors. - Assert.Equal(64, frameMetaData.ColorTableLength); + Assert.Equal(64, frameMetadata.ColorTableLength); for (int i = 0; i < image.Frames.Count; i++) { - GifFrameMetadata ifm = image.Frames[i].Metadata.GetFormatMetadata(GifFormat.Instance); - GifFrameMetadata cifm = clone.Frames[i].Metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata ifm = image.Frames[i].Metadata.GetGifMetadata(); + GifFrameMetadata cifm = clone.Frames[i].Metadata.GetGifMetadata(); Assert.Equal(ifm.ColorTableLength, cifm.ColorTableLength); Assert.Equal(ifm.FrameDelay, cifm.FrameDelay); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs similarity index 95% rename from tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs rename to tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs index b891c8ed20..a3bc5d45c4 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifFrameMetadataTests.cs @@ -6,7 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Gif { - public class GifFrameMetaDataTests + public class GifFrameMetadataTests { [Fact] public void CloneIsDeep() diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs similarity index 93% rename from tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs rename to tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index 8cb61235e3..7f1acf71e1 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -13,7 +13,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Gif { - public class GifMetaDataTests + public class GifMetadataTests { public static readonly TheoryData RatioFiles = new TheoryData @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = testFile.CreateRgba32Image(options)) { - GifMetadata metadata = image.Metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(1, metadata.Comments.Count); Assert.Equal("ImageSharp", metadata.Comments[0]); } @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = testFile.CreateRgba32Image(options)) { - GifMetadata metadata = image.Metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(0, metadata.Comments.Count); } } @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = testFile.CreateRgba32Image(options)) { - GifMetadata metadata = image.Metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(2, metadata.Comments.Count); Assert.Equal(new string('c', 349), metadata.Comments[0]); Assert.Equal("ImageSharp", metadata.Comments[1]); @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif using (Image image = decoder.Decode(Configuration.Default, memoryStream)) { - GifMetadata metadata = image.Metadata.GetFormatMetadata(GifFormat.Instance); + GifMetadata metadata = image.Metadata.GetGifMetadata(); Assert.Equal(2, metadata.Comments.Count); Assert.Equal(new string('c', 349), metadata.Comments[0]); Assert.Equal("ImageSharp", metadata.Comments[1]); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index 4b1abf9094..6b803c3ae4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -7,8 +7,6 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; -using SixLabors.Memory; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index 2485561f1e..59b6963eb9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; // ReSharper disable InconsistentNaming @@ -15,22 +17,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void DecodeBaselineJpeg(TestImageProvider provider) where TPixel : struct, IPixel { - if (SkipTest(provider)) + static void RunTest(string providerDump) { - // skipping to avoid OutOfMemoryException on CI - return; - } + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); - using (Image image = provider.GetImage(JpegDecoder)) - { + using Image image = provider.GetImage(JpegDecoder); image.DebugSave(provider); provider.Utility.TestName = DecodeBaselineJpegOutputName; image.CompareToReferenceOutput( - this.GetImageComparer(provider), + GetImageComparer(provider), provider, appendPixelTypeToFileName: false); } + + string providerDump = BasicSerializer.Serialize(provider); + RemoteExecutor.Invoke(RunTest, providerDump).Dispose(); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index 4a3ef9b956..37da32d763 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -31,6 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.InvalidAPP0721, TestImages.Jpeg.Issues.ExifGetString750Load, TestImages.Jpeg.Issues.ExifGetString750Transform, + TestImages.Jpeg.Issues.BadSubSampling1076, // LibJpeg can open this despite the invalid density units. TestImages.Jpeg.Issues.Fuzz.ArgumentOutOfRangeException825B, @@ -38,6 +39,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // LibJpeg can open this despite incorrect colorspace metadata. TestImages.Jpeg.Issues.IncorrectColorspace855, + // LibJpeg can open this despite the invalid subsampling units. + TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, + // High depth images TestImages.Jpeg.Baseline.Testorig12bit, }; @@ -71,7 +75,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.Fuzz.NullReferenceException823, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824A, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824B, - TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824D, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824E, TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824F, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs similarity index 95% rename from tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs rename to tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 1d5c3e27a4..4b845c2cbb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { // TODO: A JPEGsnoop & metadata expert should review if the Exif/Icc expectations are correct. // I'm seeing several entries with Exif-related names in images where we do not decode an exif profile. (- Anton) - public static readonly TheoryData MetaDataTestData = + public static readonly TheoryData MetadataTestData = new TheoryData { { false, TestImages.Jpeg.Progressive.Progress, 24, false, false }, @@ -58,15 +58,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg }; [Theory] - [MemberData(nameof(MetaDataTestData))] - public void MetaDataIsParsedCorrectly( + [MemberData(nameof(MetadataTestData))] + public void MetadataIsParsedCorrectly( bool useIdentify, string imagePath, int expectedPixelSize, bool exifProfilePresent, bool iccProfilePresent) { - TestMetaDataImpl( + TestMetadataImpl( useIdentify, JpegDecoder, imagePath, @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { var decoder = new JpegDecoder(); IImageInfo image = decoder.Identify(Configuration.Default, stream); - JpegMetadata meta = image.Metadata.GetFormatMetadata(JpegFormat.Instance); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } } @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var decoder = new JpegDecoder(); using (Image image = decoder.Decode(Configuration.Default, stream)) { - JpegMetadata meta = image.Metadata.GetFormatMetadata(JpegFormat.Instance); + JpegMetadata meta = image.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } } @@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - private static void TestMetaDataImpl( + private static void TestMetadataImpl( bool useIdentify, IImageDecoder decoder, string imagePath, @@ -209,16 +209,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [InlineData(false)] [InlineData(true)] - public void IgnoreMetaData_ControlsWhetherMetaDataIsParsed(bool ignoreMetaData) + public void IgnoreMetadata_ControlsWhetherMetadataIsParsed(bool ignoreMetadata) { - var decoder = new JpegDecoder { IgnoreMetadata = ignoreMetaData }; + var decoder = new JpegDecoder { IgnoreMetadata = ignoreMetadata }; // Snake.jpg has both Exif and ICC profiles defined: var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake); using (Image image = testFile.CreateRgba32Image(decoder)) { - if (ignoreMetaData) + if (ignoreMetadata) { Assert.Null(image.Metadata.ExifProfile); Assert.Null(image.Metadata.IccProfile); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index 77bc9f5404..4f155e9c3a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; // ReSharper disable InconsistentNaming @@ -16,22 +18,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : struct, IPixel { - if (SkipTest(provider)) + static void RunTest(string providerDump) { - // skipping to avoid OutOfMemoryException on CI - return; - } + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); - using (Image image = provider.GetImage(JpegDecoder)) - { + using Image image = provider.GetImage(JpegDecoder); image.DebugSave(provider); provider.Utility.TestName = DecodeProgressiveJpegOutputName; image.CompareToReferenceOutput( - this.GetImageComparer(provider), + GetImageComparer(provider), provider, appendPixelTypeToFileName: false); } + + string dump = BasicSerializer.Serialize(provider); + RemoteExecutor.Invoke(RunTest, dump).Dispose(); } } -} \ 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 a9cddebc85..90caea387d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -4,11 +4,12 @@ using System; using System.IO; using System.Linq; - +using Microsoft.DotNet.RemoteExecutor; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; @@ -23,9 +24,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; private const float BaselineTolerance = 0.001F / 100; + private const float ProgressiveTolerance = 0.2F / 100; - private ImageComparer GetImageComparer(TestImageProvider provider) + static JpegDecoderTests() + { + TestEnvironment.PrepareRemoteExecutor(); + } + + private static ImageComparer GetImageComparer(TestImageProvider provider) where TPixel : struct, IPixel { string file = provider.SourceFileOrDescription; @@ -88,23 +95,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - if (SkipTest(provider)) - { - return; - } - - // For 32 bit test environments: - provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - using (Image image = provider.GetImage(JpegDecoder)) + static void RunTest(string providerDump) { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + using Image image = provider.GetImage(JpegDecoder); image.DebugSave(provider); provider.Utility.TestName = DecodeBaselineJpegOutputName; image.CompareToReferenceOutput(ImageComparer.Tolerant(BaselineTolerance), provider, appendPixelTypeToFileName: false); } - provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); + string dump = BasicSerializer.Serialize(provider); + RemoteExecutor.Invoke(RunTest, dump).Dispose(); } // DEBUG ONLY! diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 639ec520ec..ccfde3b465 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg memStream.Position = 0; using (var output = Image.Load(memStream)) { - JpegMetadata meta = output.Metadata.GetFormatMetadata(JpegFormat.Instance); + JpegMetadata meta = output.Metadata.GetJpegMetadata(); Assert.Equal(quality, meta.Quality); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs similarity index 93% rename from tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs rename to tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs index 59ad571f49..50a2a44163 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs @@ -6,7 +6,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - public class JpegMetaDataTests + public class JpegMetadataTests { [Fact] public void CloneIsDeep() diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs index 1d7ca746f6..44545092fd 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 91cd80d144..a4929fe809 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -9,7 +9,6 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs index 296f424fa5..16fd352a99 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs @@ -3,7 +3,6 @@ using System.Linq; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index e976d5a768..660d5b7246 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -54,7 +54,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [InlineData((uint)PngChunkType.Header)] // IHDR [InlineData((uint)PngChunkType.Palette)] // PLTE // [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this - [InlineData((uint)PngChunkType.End)] // IEND public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType) { string chunkName = GetChunkTypeName(chunkType); @@ -74,26 +73,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } - [Theory] - [InlineData((uint)PngChunkType.Gamma)] // gAMA - [InlineData((uint)PngChunkType.Transparency)] // tRNS - [InlineData((uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks. - //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this - public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType) - { - string chunkName = GetChunkTypeName(chunkType); - - using (var memStream = new MemoryStream()) - { - WriteHeaderChunk(memStream); - WriteChunk(memStream, chunkName); - WriteDataChunk(memStream); - - var decoder = new PngDecoder(); - decoder.Decode(null, memStream); - } - } - private static string GetChunkTypeName(uint value) { var data = new byte[4]; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 91b1ef2c17..3b05b00ce0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -4,11 +4,11 @@ // ReSharper disable InconsistentNaming using System.IO; - +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; namespace SixLabors.ImageSharp.Tests.Formats.Png @@ -37,11 +37,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.VimImage2, TestImages.Png.Rgb24BppTrans, - TestImages.Png.GrayAlpha8Bit, + TestImages.Png.GrayA8Bit, TestImages.Png.Gray1BitTrans, TestImages.Png.Bad.ZlibOverflow, TestImages.Png.Bad.ZlibOverflow2, TestImages.Png.Bad.ZlibZtxtBadHeader, + TestImages.Png.Bad.Issue1047_BadEndChunk }; public static readonly string[] TestImages48Bpp = @@ -56,9 +57,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.Rgb48BppTrans }; - public static readonly string[] TestImagesGray16Bit = + public static readonly string[] TestImagesL16Bit = { - TestImages.Png.Gray16Bit, + TestImages.Png.L16Bit, }; public static readonly string[] TestImagesGrayAlpha16Bit = @@ -67,14 +68,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.GrayTrns16BitInterlaced }; - public static readonly string[] TestImagesGray8BitInterlaced = + public static readonly string[] TestImagesL8BitInterlaced = { TestImages.Png.GrayAlpha1BitInterlaced, TestImages.Png.GrayAlpha2BitInterlaced, TestImages.Png.Gray4BitInterlaced, - TestImages.Png.GrayAlpha8BitInterlaced + TestImages.Png.GrayA8BitInterlaced }; + public static readonly string[] TestImagesIssue1014 = + { + TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2, + TestImages.Png.Issue1014_3, TestImages.Png.Issue1014_4, + TestImages.Png.Issue1014_5, TestImages.Png.Issue1014_6 + }; + [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) @@ -83,7 +91,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = provider.GetImage(new PngDecoder())) { image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact); + + // We don't have another x-plat reference decoder that can be compared for this image. + if (provider.Utility.SourceFileOrDescription == TestImages.Png.Bad.Issue1047_BadEndChunk) + { + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider, ImageComparer.Exact, (IImageDecoder)SystemDrawingReferenceDecoder.Instance); + } + } + else + { + image.CompareToOriginal(provider, ImageComparer.Exact); + } } } @@ -124,8 +144,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFileCollection(nameof(TestImagesGray8BitInterlaced), PixelTypes.Rgba32)] - public void Decoder_Gray8bitInterlaced(TestImageProvider provider) + [WithFileCollection(nameof(TestImagesL8BitInterlaced), PixelTypes.Rgba32)] + public void Decoder_L8bitInterlaced(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new PngDecoder())) @@ -136,8 +156,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFileCollection(nameof(TestImagesGray16Bit), PixelTypes.Rgb48)] - public void Decode_Gray16Bit(TestImageProvider provider) + [WithFileCollection(nameof(TestImagesL16Bit), PixelTypes.Rgb48)] + public void Decode_L16Bit(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new PngDecoder())) @@ -160,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFile(TestImages.Png.GrayAlpha8BitInterlaced, PixelTypes)] + [WithFile(TestImages.Png.GrayA8BitInterlaced, PixelTypes)] public void Decoder_CanDecodeGrey8bitWithAlpha(TestImageProvider provider) where TPixel : struct, IPixel { @@ -199,5 +219,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } + + [Theory] + [WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)] + public void Issue1014(TestImageProvider provider) + where TPixel : struct, IPixel + { + System.Exception ex = Record.Exception( + () => + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + }); + Assert.Null(ex); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 04503330e9..cacb3e42fe 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { TestImages.Png.Gray1BitTrans, PngBitDepth.Bit1, PngColorType.Grayscale }, { TestImages.Png.Gray2BitTrans, PngBitDepth.Bit2, PngColorType.Grayscale }, { TestImages.Png.Gray4BitTrans, PngBitDepth.Bit4, PngColorType.Grayscale }, - { TestImages.Png.Gray8BitTrans, PngBitDepth.Bit8, PngColorType.Grayscale }, + { TestImages.Png.L8BitTrans, PngBitDepth.Bit8, PngColorType.Grayscale }, { TestImages.Png.GrayTrns16BitInterlaced, PngBitDepth.Bit16, PngColorType.Grayscale }, { TestImages.Png.Rgb24BppTrans, PngBitDepth.Bit8, PngColorType.Rgb }, { TestImages.Png.Rgb48BppTrans, PngBitDepth.Bit16, PngColorType.Rgb } @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png /// public static readonly TheoryData CompressionLevels = new TheoryData { - 1, 2, 3, 4, 5, 6, 7, 8, 9 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; public static readonly TheoryData PaletteSizes = new TheoryData @@ -181,6 +181,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void WorksWithAllBitDepths(TestImageProvider provider, PngColorType pngColorType, PngBitDepth pngBitDepth) where TPixel : struct, IPixel { + // TODO: Investigate WuQuantizer to see if we can reduce memory pressure. + if (TestEnvironment.RunsOnCI && !TestEnvironment.Is64BitProcess) + { + return; + } + foreach (PngInterlaceMode interlaceMode in InterlaceMode) { TestPngEncoderCore( @@ -243,6 +249,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) where TPixel : struct, IPixel { + // TODO: Investigate WuQuantizer to see if we can reduce memory pressure. + if (TestEnvironment.RunsOnCI && !TestEnvironment.Is64BitProcess) + { + return; + } + foreach (PngInterlaceMode interlaceMode in InterlaceMode) { TestPngEncoderCore( @@ -323,7 +335,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png memStream.Position = 0; using (var output = Image.Load(memStream)) { - PngMetadata meta = output.Metadata.GetFormatMetadata(PngFormat.Instance); + PngMetadata meta = output.Metadata.GetPngMetadata(); Assert.Equal(pngBitDepth, meta.BitDepth); } @@ -340,7 +352,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(imagePath); using (Image input = testFile.CreateRgba32Image()) { - PngMetadata inMeta = input.Metadata.GetFormatMetadata(PngFormat.Instance); + PngMetadata inMeta = input.Metadata.GetPngMetadata(); Assert.True(inMeta.HasTransparency); using (var memStream = new MemoryStream()) @@ -349,7 +361,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png memStream.Position = 0; using (var output = Image.Load(memStream)) { - PngMetadata outMeta = output.Metadata.GetFormatMetadata(PngFormat.Instance); + PngMetadata outMeta = output.Metadata.GetPngMetadata(); Assert.True(outMeta.HasTransparency); switch (pngColorType) @@ -357,13 +369,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png case PngColorType.Grayscale: if (pngBitDepth.Equals(PngBitDepth.Bit16)) { - Assert.True(outMeta.TransparentGray16.HasValue); - Assert.Equal(inMeta.TransparentGray16, outMeta.TransparentGray16); + Assert.True(outMeta.TransparentL16.HasValue); + Assert.Equal(inMeta.TransparentL16, outMeta.TransparentL16); } else { - Assert.True(outMeta.TransparentGray8.HasValue); - Assert.Equal(inMeta.TransparentGray8, outMeta.TransparentGray8); + Assert.True(outMeta.TransparentL8.HasValue); + Assert.Equal(inMeta.TransparentL8, outMeta.TransparentL8); } break; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs similarity index 98% rename from tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs rename to tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index dfa7fd2922..4b11ad3e27 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -11,7 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Png { - public class PngMetaDataTests + public class PngMetadataTests { public static readonly TheoryData RatioFiles = new TheoryData @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFile(TestImages.Png.PngWithMetaData, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)] public void Decoder_CanReadTextData(TestImageProvider provider) where TPixel : struct, IPixel { @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFile(TestImages.Png.PngWithMetaData, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)] public void Encoder_PreservesTextData(TestImageProvider provider) where TPixel : struct, IPixel { @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithFile(TestImages.Png.PngWithMetaData, PixelTypes.Rgba32)] + [WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)] public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index e26aaf8e65..1f8147ea95 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png // using (Image source = provider.GetImage()) // using (MemoryStream ms = new MemoryStream()) // { - // source.MetaData.Quality = 256; + // source.Metadata.Quality = 256; // source.Save(ms, new PngEncoder(), new PngEncoderOptions { // Threshold = 200 // }); diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs new file mode 100644 index 0000000000..03ad10de40 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs @@ -0,0 +1,197 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tga +{ + using static TestImages.Tga; + + public class TgaDecoderTests + { + [Theory] + [WithFile(Grey, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_MonoChrome(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit15, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_15Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit15Rle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_15Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit16, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_16Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit16PalRle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithPalette_16Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_24Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24RleTopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_WithTopLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24TopLeft, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Palette_WithTopLeftOrigin_24Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_Uncompressed_32Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(GreyRle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_MonoChrome(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit16Rle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_16Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24Rle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit32Rle, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_RunLengthEncoded_32Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit16Pal, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPalette_16Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + + [Theory] + [WithFile(Bit24Pal, PixelTypes.Rgba32)] + public void TgaDecoder_CanDecode_WithPalette_24Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new TgaDecoder())) + { + image.DebugSave(provider); + TgaTestUtils.CompareWithReferenceDecoder(provider, image); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs new file mode 100644 index 0000000000..9d34684f7a --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaEncoderTests.cs @@ -0,0 +1,148 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using System.IO; + +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tga +{ + using static TestImages.Tga; + + public class TgaEncoderTests + { + public static readonly TheoryData BitsPerPixel = + new TheoryData + { + TgaBitsPerPixel.Pixel24, + TgaBitsPerPixel.Pixel32 + }; + + public static readonly TheoryData TgaBitsPerPixelFiles = + new TheoryData + { + { Grey, TgaBitsPerPixel.Pixel8 }, + { Bit32, TgaBitsPerPixel.Pixel32 }, + { Bit24, TgaBitsPerPixel.Pixel24 }, + { Bit16, TgaBitsPerPixel.Pixel16 }, + }; + + [Theory] + [MemberData(nameof(TgaBitsPerPixelFiles))] + public void Encode_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) + { + var options = new TgaEncoder(); + + TestFile testFile = TestFile.Create(imagePath); + using (Image input = testFile.CreateRgba32Image()) + { + using (var memStream = new MemoryStream()) + { + input.Save(memStream, options); + memStream.Position = 0; + using (Image output = Image.Load(memStream)) + { + TgaMetadata meta = output.Metadata.GetTgaMetadata(); + Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); + } + } + } + } + + [Theory] + [MemberData(nameof(TgaBitsPerPixelFiles))] + public void Encode_WithCompression_PreserveBitsPerPixel(string imagePath, TgaBitsPerPixel bmpBitsPerPixel) + { + var options = new TgaEncoder() + { + Compression = TgaCompression.RunLength + }; + + TestFile testFile = TestFile.Create(imagePath); + using (Image input = testFile.CreateRgba32Image()) + { + using (var memStream = new MemoryStream()) + { + input.Save(memStream, options); + memStream.Position = 0; + using (Image output = Image.Load(memStream)) + { + TgaMetadata meta = output.Metadata.GetTgaMetadata(); + Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); + } + } + } + } + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit8_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) + // using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false, compareTolerance: 0.03f); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit16_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None, useExactComparer: false); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit24_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit32_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.None); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit8_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel8) + // using tolerant comparer here. The results from magick differ slightly. Maybe a different ToGrey method is used. The image looks otherwise ok. + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false, compareTolerance: 0.03f); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit16_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel16) + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength, useExactComparer: false); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit24_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel24) + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); + + [Theory] + [WithFile(Bit32, PixelTypes.Rgba32)] + public void Encode_Bit32_WithRunLengthEncoding_Works(TestImageProvider provider, TgaBitsPerPixel bitsPerPixel = TgaBitsPerPixel.Pixel32) + where TPixel : struct, IPixel => TestTgaEncoderCore(provider, bitsPerPixel, TgaCompression.RunLength); + + private static void TestTgaEncoderCore( + TestImageProvider provider, + TgaBitsPerPixel bitsPerPixel, + TgaCompression compression = TgaCompression.None, + bool useExactComparer = true, + float compareTolerance = 0.01f) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var encoder = new TgaEncoder { BitsPerPixel = bitsPerPixel, Compression = compression}; + + using (var memStream = new MemoryStream()) + { + image.Save(memStream, encoder); + memStream.Position = 0; + using (var encodedImage = (Image)Image.Load(memStream)) + { + TgaTestUtils.CompareWithReferenceDecoder(provider, encodedImage, useExactComparer, compareTolerance); + } + } + } + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs new file mode 100644 index 0000000000..c227b79576 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Formats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Tga +{ + public class TgaFileHeaderTests + { + private static readonly byte[] Data = { + 0, + 0, + 15 // invalid tga image type + }; + + private MemoryStream Stream { get; } = new MemoryStream(Data); + + [Fact] + public void ImageLoad_WithInvalidImageType_Throws_UnknownImageFormatException() + { + Assert.Throws(() => + { + using (Image.Load(Configuration.Default, this.Stream, out IImageFormat _)) + { + } + }); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs new file mode 100644 index 0000000000..a2f2e86d7d --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; + +using ImageMagick; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +namespace SixLabors.ImageSharp.Tests.Formats.Tga +{ + public static class TgaTestUtils + { + public static void CompareWithReferenceDecoder(TestImageProvider provider, + Image image, + bool useExactComparer = true, + float compareTolerance = 0.01f) + where TPixel : struct, IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + TestFile testFile = TestFile.Create(path); + Image magickImage = DecodeWithMagick(Configuration.Default, new FileInfo(testFile.FullPath)); + if (useExactComparer) + { + ImageComparer.Exact.VerifySimilarity(magickImage, image); + } + else + { + ImageComparer.Tolerant(compareTolerance).VerifySimilarity(magickImage, image); + } + } + + public static Image DecodeWithMagick(Configuration configuration, FileInfo fileInfo) + where TPixel : struct, IPixel + { + using (var magickImage = new MagickImage(fileInfo)) + { + var result = new Image(configuration, magickImage.Width, magickImage.Height); + Span resultPixels = result.GetPixelSpan(); + + using (IPixelCollection pixels = magickImage.GetPixelsUnsafe()) + { + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + + PixelOperations.Instance.FromRgba32Bytes( + configuration, + data, + resultPixels, + resultPixels.Length); + } + + return result; + } + } + } +} diff --git a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs index 6ff38626d6..e4892e5618 100644 --- a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs @@ -1,21 +1,90 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests { public class GraphicsOptionsTests { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + private readonly GraphicsOptions newGraphicsOptions = new GraphicsOptions(); + private readonly GraphicsOptions cloneGraphicsOptions = new GraphicsOptions().DeepClone(); + + [Fact] + public void CloneGraphicsOptionsIsNotNull() => Assert.True(this.cloneGraphicsOptions != null); + + [Fact] + public void DefaultGraphicsOptionsAntialias() + { + Assert.True(this.newGraphicsOptions.Antialias); + Assert.True(this.cloneGraphicsOptions.Antialias); + } + + [Fact] + public void DefaultGraphicsOptionsAntialiasSuppixelDepth() + { + const int Expected = 16; + Assert.Equal(Expected, this.newGraphicsOptions.AntialiasSubpixelDepth); + Assert.Equal(Expected, this.cloneGraphicsOptions.AntialiasSubpixelDepth); + } + + [Fact] + public void DefaultGraphicsOptionsBlendPercentage() + { + const float Expected = 1F; + Assert.Equal(Expected, this.newGraphicsOptions.BlendPercentage); + Assert.Equal(Expected, this.cloneGraphicsOptions.BlendPercentage); + } + [Fact] - public void IsOpaqueColor() + public void DefaultGraphicsOptionsColorBlendingMode() { - Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Red)); - Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(Rgba32.Red)); - Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Transparent)); - Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); - Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); + const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal; + Assert.Equal(Expected, this.newGraphicsOptions.ColorBlendingMode); + Assert.Equal(Expected, this.cloneGraphicsOptions.ColorBlendingMode); + } + + [Fact] + public void DefaultGraphicsOptionsAlphaCompositionMode() + { + const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver; + Assert.Equal(Expected, this.newGraphicsOptions.AlphaCompositionMode); + Assert.Equal(Expected, this.cloneGraphicsOptions.AlphaCompositionMode); + } + + [Fact] + public void NonDefaultClone() + { + var expected = new GraphicsOptions + { + AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop, + Antialias = false, + AntialiasSubpixelDepth = 23, + BlendPercentage = .25F, + ColorBlendingMode = PixelColorBlendingMode.HardLight, + }; + + GraphicsOptions actual = expected.DeepClone(); + + Assert.Equal(expected, actual, graphicsOptionsComparer); + } + + [Fact] + public void CloneIsDeep() + { + var expected = new GraphicsOptions(); + GraphicsOptions actual = expected.DeepClone(); + + actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop; + actual.Antialias = false; + actual.AntialiasSubpixelDepth = 23; + actual.BlendPercentage = .25F; + actual.ColorBlendingMode = PixelColorBlendingMode.HardLight; + + Assert.NotEqual(expected, actual, graphicsOptionsComparer); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs index 018fabd982..16a27a9cea 100644 --- a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; + using Xunit; namespace SixLabors.ImageSharp.Tests.Helpers @@ -131,6 +133,23 @@ namespace SixLabors.ImageSharp.Tests.Helpers Assert.Equal(expected, actual); } + [Theory] + [InlineData(0.2f, 0.7f, 0.1f, 256, 140)] + [InlineData(0.5f, 0.5f, 0.5f, 256, 128)] + [InlineData(0.5f, 0.5f, 0.5f, 65536, 32768)] + [InlineData(0.2f, 0.7f, 0.1f, 65536, 36069)] + public void GetBT709Luminance_WithVector4(float x, float y, float z, int luminanceLevels, int expected) + { + // arrange + var vector = new Vector4(x, y, z, 0.0f); + + // act + int actual = ImageMaths.GetBT709Luminance(ref vector, luminanceLevels); + + // assert + Assert.Equal(expected, actual); + } + // TODO: We need to test all ImageMaths methods! } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs new file mode 100644 index 0000000000..3cfce6b8e2 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced.ParallelUtils; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class ParallelExecutionSettingsTests + { + [Theory] + [InlineData(-3, true)] + [InlineData(-2, true)] + [InlineData(-1, false)] + [InlineData(0, true)] + [InlineData(1, false)] + [InlineData(5, false)] + public void Constructor_MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) + { + if (throws) + { + Assert.Throws( + () => + { + _ = new ParallelExecutionSettings( + maxDegreeOfParallelism, + Configuration.Default.MemoryAllocator); + }); + } + else + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + Configuration.Default.MemoryAllocator); + Assert.Equal(maxDegreeOfParallelism, parallelSettings.MaxDegreeOfParallelism); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs index aeadfcebb5..93ddaefe6f 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -7,11 +7,9 @@ using System.Linq; using System.Numerics; using System.Threading; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; @@ -332,7 +330,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers }); // Assert: - TestImageExtensions.CompareBuffers(expected.Span, actual.Span); + TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan()); } } diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs index 45067f82fe..0bb3f49d64 100644 --- a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers Span span = buffer.GetMultiRowSpan(rows); - ref int expected0 = ref buffer.Span[min * width]; + ref int expected0 = ref buffer.GetSpan()[min * width]; int expectedLength = (max - min) * width; ref int actual0 = ref span[0]; diff --git a/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs b/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs index 2d5e81173b..57e9dbad6e 100644 --- a/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs +++ b/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs @@ -4,7 +4,7 @@ using System; using System.IO; using SixLabors.ImageSharp.IO; -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; using Xunit; namespace SixLabors.ImageSharp.Tests.IO diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs index e972012e29..e41bb4c176 100644 --- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs +++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs @@ -312,8 +312,8 @@ namespace SixLabors.ImageSharp.Tests { // TODO: all metadata classes should be equatable! - GifFrameMetadata aData = a.Metadata.GetFormatMetadata(GifFormat.Instance); - GifFrameMetadata bData = b.Metadata.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata aData = a.Metadata.GetGifMetadata(); + GifFrameMetadata bData = b.Metadata.GetGifMetadata(); Assert.Equal(aData.DisposalMethod, bData.DisposalMethod); Assert.Equal(aData.FrameDelay, bData.FrameDelay); diff --git a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs index e1c4a419e1..28196c0da1 100644 --- a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs index 4d3a229c55..39795fbf3d 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs @@ -6,7 +6,6 @@ using System; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs index 19887d9bcc..0632d74407 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -6,7 +6,6 @@ using System; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs index 980ed17ceb..d9d1eb7c49 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs @@ -7,7 +7,6 @@ using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index ea99573141..a018af6ed0 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -11,8 +11,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Shapes; -using SixLabors.ImageSharp.Processing; using Xunit; // ReSharper disable InconsistentNaming @@ -42,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests } this.bitmap = bitmap; - var rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height); + var rectangle = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height); this.bmpData = bitmap.LockBits(rectangle, ImageLockMode.ReadWrite, bitmap.PixelFormat); this.length = bitmap.Width * bitmap.Height; } @@ -60,13 +58,13 @@ namespace SixLabors.ImageSharp.Tests { this.bitmap.UnlockBits(this.bmpData); } - + this.IsDisposed = true; } public override unsafe Span GetSpan() { - void* ptr = (void*) this.bmpData.Scan0; + void* ptr = (void*)this.bmpData.Scan0; return new Span(ptr, this.length); } @@ -119,7 +117,11 @@ namespace SixLabors.ImageSharp.Tests using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) { Assert.Equal(memory, image.GetPixelMemory()); - image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10))); + image.GetPixelSpan().Fill(bg); + for (var i = 10; i < 20; i++) + { + image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); + } } Assert.False(memoryManager.IsDisposed); @@ -150,7 +152,12 @@ namespace SixLabors.ImageSharp.Tests using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) { Assert.Equal(memoryManager.Memory, image.GetPixelMemory()); - image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10))); + + image.GetPixelSpan().Fill(bg); + for (var i = 10; i < 20; i++) + { + image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg); + } } Assert.True(memoryManager.IsDisposed); @@ -164,7 +171,7 @@ namespace SixLabors.ImageSharp.Tests } private static bool ShouldSkipBitmapTest => - !TestEnvironment.Is64BitProcess || TestHelpers.ImageSharpBuiltAgainst != "netcoreapp2.1"; + !TestEnvironment.Is64BitProcess || (TestHelpers.ImageSharpBuiltAgainst != "netcoreapp3.1" && TestHelpers.ImageSharpBuiltAgainst != "netcoreapp2.1"); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 60384c0578..1e48f14c86 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests configuration.MemoryAllocator = new TestMemoryAllocator(dirtyValue); var metadata = new ImageMetadata(); - using (Image image = Image.CreateUninitialized(configuration, 21, 22, metadata)) + using (Image image = Image.CreateUninitialized(configuration, 21, 22, metadata)) { Assert.Equal(21, image.Width); Assert.Equal(22, image.Height); diff --git a/tests/ImageSharp.Tests/ImageInfoTests.cs b/tests/ImageSharp.Tests/ImageInfoTests.cs index 67804a18fd..bde5f7b6a5 100644 --- a/tests/ImageSharp.Tests/ImageInfoTests.cs +++ b/tests/ImageSharp.Tests/ImageInfoTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 1ac5f8085a..34cdca49a1 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,27 +1,26 @@ - + - netcoreapp2.1;net462;net472 + netcoreapp3.1;netcoreapp2.1;net472 True - latest - full - portable True SixLabors.ImageSharp.Tests AnyCPU;x64;x86 SixLabors.ImageSharp.Tests + + true - - - - + - + + + + @@ -36,10 +35,5 @@ - - - - - diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject b/tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject deleted file mode 100644 index f015b4b86e..0000000000 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject +++ /dev/null @@ -1,9 +0,0 @@ - - - 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 deleted file mode 100644 index 5a590018e5..0000000000 --- a/tests/ImageSharp.Tests/Issues/Issue412.cs +++ /dev/null @@ -1,51 +0,0 @@ -using SixLabors.Primitives; - -using Xunit; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; - -namespace SixLabors.ImageSharp.Tests.Issues -{ - public class Issue412 - { - [Theory] - [WithBlankImages(40, 30, PixelTypes.Rgba32)] - public void AllPixelsExpectedToBeRedWhenAntialiasedDisabled(TestImageProvider provider) where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate( - context => - { - for (var i = 0; i < 40; ++i) - { - context.DrawLines( - new GraphicsOptions(false), - Color.Black, - 1, - new PointF(i, 0.1066f), - new PointF(i, 10.1066f)); - - context.DrawLines( - new GraphicsOptions(false), - Color.Red, - 1, - new PointF(i, 15.1066f), - new PointF(i, 25.1066f)); - } - }); - - image.DebugSave(provider); - for (var y = 15; y < 25; y++) - { - for (var x = 0; x < 40; x++) - { - TPixel red = Color.Red.ToPixel(); - - Assert.True(red.Equals(image[x, y]), $"expected {Color.Red} but found {image[x, y]} at [{x}, {y}]"); - } - } - } - } - } -} diff --git a/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs new file mode 100644 index 0000000000..227d627784 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Alocators/ArrayPoolMemoryAllocatorTests.cs @@ -0,0 +1,273 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; +using Microsoft.Win32; +using SixLabors.ImageSharp.Tests; +using Xunit; + +namespace SixLabors.ImageSharp.Memory.Tests +{ + public class ArrayPoolMemoryAllocatorTests + { + private const int MaxPooledBufferSizeInBytes = 2048; + + private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2; + + /// + /// Contains SUT for in-process tests. + /// + private MemoryAllocatorFixture LocalFixture { get; } = new MemoryAllocatorFixture(); + + /// + /// Contains SUT for tests executed by , + /// recreated in each external process. + /// + private static MemoryAllocatorFixture StaticFixture { get; } = new MemoryAllocatorFixture(); + + static ArrayPoolMemoryAllocatorTests() + { + TestEnvironment.PrepareRemoteExecutor(); + } + + public class BufferTests : BufferTestSuite + { + public BufferTests() + : base(new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes)) + { + } + } + + public class Constructor + { + [Fact] + public void WhenBothParametersPassedByUser() + { + var mgr = new ArrayPoolMemoryAllocator(1111, 666); + Assert.Equal(1111, mgr.MaxPoolSizeInBytes); + Assert.Equal(666, mgr.PoolSelectorThresholdInBytes); + } + + [Fact] + public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdValueIsAutoCalculated() + { + var mgr = new ArrayPoolMemoryAllocator(5000); + Assert.Equal(5000, mgr.MaxPoolSizeInBytes); + Assert.True(mgr.PoolSelectorThresholdInBytes < mgr.MaxPoolSizeInBytes); + } + + [Fact] + public void When_PoolSelectorThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_ExceptionIsThrown() + { + Assert.ThrowsAny(() => new ArrayPoolMemoryAllocator(100, 200)); + } + } + + [Theory] + [InlineData(32)] + [InlineData(512)] + [InlineData(MaxPooledBufferSizeInBytes - 1)] + public void SmallBuffersArePooled_OfByte(int size) + { + Assert.True(this.LocalFixture.CheckIsRentingPooledBuffer(size)); + } + + [Theory] + [InlineData(128 * 1024 * 1024)] + [InlineData(MaxPooledBufferSizeInBytes + 1)] + public void LargeBuffersAreNotPooled_OfByte(int size) + { + static void RunTest(string sizeStr) + { + int size = int.Parse(sizeStr); + StaticFixture.CheckIsRentingPooledBuffer(size); + } + + RemoteExecutor.Invoke(RunTest, size.ToString()).Dispose(); + } + + [Fact] + public unsafe void SmallBuffersArePooled_OfBigValueType() + { + int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) - 1; + + Assert.True(this.LocalFixture.CheckIsRentingPooledBuffer(count)); + } + + [Fact] + public unsafe void LaregeBuffersAreNotPooled_OfBigValueType() + { + int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) + 1; + + Assert.False(this.LocalFixture.CheckIsRentingPooledBuffer(count)); + } + + [Theory] + [InlineData(AllocationOptions.None)] + [InlineData(AllocationOptions.Clean)] + public void CleaningRequests_AreControlledByAllocationParameter_Clean(AllocationOptions options) + { + MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator; + using (IMemoryOwner firstAlloc = memoryAllocator.Allocate(42)) + { + firstAlloc.GetSpan().Fill(666); + } + + using (IMemoryOwner secondAlloc = memoryAllocator.Allocate(42, options)) + { + int expected = options == AllocationOptions.Clean ? 0 : 666; + Assert.Equal(expected, secondAlloc.GetSpan()[0]); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) + { + MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator; + IMemoryOwner buffer = memoryAllocator.Allocate(32); + ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.GetSpan()); + + if (!keepBufferAlive) + { + buffer.Dispose(); + } + + memoryAllocator.ReleaseRetainedResources(); + + buffer = memoryAllocator.Allocate(32); + + Assert.False(Unsafe.AreSame(ref ptrToPrev0, ref buffer.GetReference())); + } + + [Fact] + public void ReleaseRetainedResources_DisposingPreviouslyAllocatedBuffer_IsAllowed() + { + MemoryAllocator memoryAllocator = this.LocalFixture.MemoryAllocator; + IMemoryOwner buffer = memoryAllocator.Allocate(32); + memoryAllocator.ReleaseRetainedResources(); + buffer.Dispose(); + } + + + [Fact] + public void AllocationOverLargeArrayThreshold_UsesDifferentPool() + { + static void RunTest() + { + const int ArrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int); + + IMemoryOwner small = StaticFixture.MemoryAllocator.Allocate(ArrayLengthThreshold - 1); + ref int ptr2Small = ref small.GetReference(); + small.Dispose(); + + IMemoryOwner large = StaticFixture.MemoryAllocator.Allocate(ArrayLengthThreshold + 1); + + Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference())); + } + + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [Fact] + public void CreateWithAggressivePooling() + { + static void RunTest() + { + StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithAggressivePooling(); + Assert.True(StaticFixture.CheckIsRentingPooledBuffer(4096 * 4096)); + } + + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [Fact] + public void CreateDefault() + { + static void RunTest() + { + StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault(); + + Assert.False(StaticFixture.CheckIsRentingPooledBuffer(2 * 4096 * 4096)); + Assert.True(StaticFixture.CheckIsRentingPooledBuffer(2048 * 2048)); + } + + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [Fact] + public void CreateWithModeratePooling() + { + static void RunTest() + { + StaticFixture.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); + Assert.False(StaticFixture.CheckIsRentingPooledBuffer(2048 * 2048)); + Assert.True(StaticFixture.CheckIsRentingPooledBuffer(1024 * 16)); + } + + RemoteExecutor.Invoke(RunTest).Dispose(); + } + + [Theory] + [InlineData(-1)] + [InlineData((int.MaxValue / SizeOfLargeStruct) + 1)] + public void AllocateIncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) + { + ArgumentOutOfRangeException ex = Assert.Throws(() => + this.LocalFixture.MemoryAllocator.Allocate(length)); + Assert.Equal("length", ex.ParamName); + } + + [Theory] + [InlineData(-1)] + public void AllocateManagedByteBuffer_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) + { + ArgumentOutOfRangeException ex = Assert.Throws(() => + this.LocalFixture.MemoryAllocator.AllocateManagedByteBuffer(length)); + Assert.Equal("length", ex.ParamName); + } + + private class MemoryAllocatorFixture + { + public MemoryAllocator MemoryAllocator { get; set; } = + new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); + + /// + /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location. + /// + public bool CheckIsRentingPooledBuffer(int length) + where T : struct + { + IMemoryOwner buffer = MemoryAllocator.Allocate(length); + ref T ptrToPrevPosition0 = ref buffer.GetReference(); + buffer.Dispose(); + + buffer = MemoryAllocator.Allocate(length); + bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.GetReference()); + buffer.Dispose(); + + return sameBuffers; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct SmallStruct + { + private readonly uint dummy; + } + + private const int SizeOfLargeStruct = MaxPooledBufferSizeInBytes / 5; + + [StructLayout(LayoutKind.Explicit, Size = SizeOfLargeStruct)] + private struct LargeStruct + { + } + } +} diff --git a/tests/ImageSharp.Tests/Memory/Alocators/BufferExtensions.cs b/tests/ImageSharp.Tests/Memory/Alocators/BufferExtensions.cs new file mode 100644 index 0000000000..8073d069d6 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Alocators/BufferExtensions.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Memory.Tests +{ + internal static class BufferExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetSpan(this IMemoryOwner buffer) + => buffer.Memory.Span; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Length(this IMemoryOwner buffer) + => buffer.GetSpan().Length; + + public static ref T GetReference(this IMemoryOwner buffer) + where T : struct => + ref MemoryMarshal.GetReference(buffer.GetSpan()); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/Alocators/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/Alocators/BufferTestSuite.cs new file mode 100644 index 0000000000..4590bbe974 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Alocators/BufferTestSuite.cs @@ -0,0 +1,318 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Memory.Tests +{ + /// + /// Inherit this class to test an implementation (provided by ). + /// + public abstract class BufferTestSuite + { + protected BufferTestSuite(MemoryAllocator memoryAllocator) + { + this.MemoryAllocator = memoryAllocator; + } + + protected MemoryAllocator MemoryAllocator { get; } + + public struct CustomStruct : IEquatable + { + public long A; + + public byte B; + + public float C; + + public CustomStruct(long a, byte b, float c) + { + this.A = a; + this.B = b; + this.C = c; + } + + public bool Equals(CustomStruct other) + { + return this.A == other.A && this.B == other.B && this.C.Equals(other.C); + } + + public override bool Equals(object obj) + { + return obj is CustomStruct other && this.Equals(other); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = this.A.GetHashCode(); + hashCode = (hashCode * 397) ^ this.B.GetHashCode(); + hashCode = (hashCode * 397) ^ this.C.GetHashCode(); + return hashCode; + } + } + } + + public static readonly TheoryData LenthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; + + [Theory] + [MemberData(nameof(LenthValues))] + public void HasCorrectLength_byte(int desiredLength) + { + this.TestHasCorrectLength(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void HasCorrectLength_float(int desiredLength) + { + this.TestHasCorrectLength(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void HasCorrectLength_CustomStruct(int desiredLength) + { + this.TestHasCorrectLength(desiredLength); + } + + private void TestHasCorrectLength(int desiredLength) + where T : struct + { + using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(desiredLength)) + { + Assert.Equal(desiredLength, buffer.GetSpan().Length); + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void CanAllocateCleanBuffer_byte(int desiredLength) + { + this.TestCanAllocateCleanBuffer(desiredLength, false); + this.TestCanAllocateCleanBuffer(desiredLength, true); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void CanAllocateCleanBuffer_double(int desiredLength) + { + this.TestCanAllocateCleanBuffer(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void CanAllocateCleanBuffer_CustomStruct(int desiredLength) + { + this.TestCanAllocateCleanBuffer(desiredLength); + } + + private IMemoryOwner Allocate(int desiredLength, AllocationOptions options, bool managedByteBuffer) + where T : struct + { + if (managedByteBuffer) + { + if (!(this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength, options) is IMemoryOwner buffer)) + { + throw new InvalidOperationException("typeof(T) != typeof(byte)"); + } + + return buffer; + } + + return this.MemoryAllocator.Allocate(desiredLength, options); + } + + private void TestCanAllocateCleanBuffer(int desiredLength, bool testManagedByteBuffer = false) + where T : struct, IEquatable + { + ReadOnlySpan expected = new T[desiredLength]; + + for (int i = 0; i < 10; i++) + { + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.Clean, testManagedByteBuffer)) + { + Assert.True(buffer.GetSpan().SequenceEqual(expected)); + } + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void SpanPropertyIsAlwaysTheSame_int(int desiredLength) + { + this.TestSpanPropertyIsAlwaysTheSame(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void SpanPropertyIsAlwaysTheSame_byte(int desiredLength) + { + this.TestSpanPropertyIsAlwaysTheSame(desiredLength, false); + this.TestSpanPropertyIsAlwaysTheSame(desiredLength, true); + } + + private void TestSpanPropertyIsAlwaysTheSame(int desiredLength, bool testManagedByteBuffer = false) + where T : struct + { + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) + { + ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan()); + ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan()); + ref T c = ref MemoryMarshal.GetReference(buffer.GetSpan()); + + Assert.True(Unsafe.AreSame(ref a, ref b)); + Assert.True(Unsafe.AreSame(ref b, ref c)); + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void WriteAndReadElements_float(int desiredLength) + { + this.TestWriteAndReadElements(desiredLength, x => x * 1.2f); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void WriteAndReadElements_byte(int desiredLength) + { + this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), false); + this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), true); + } + + private void TestWriteAndReadElements(int desiredLength, Func getExpectedValue, bool testManagedByteBuffer = false) + where T : struct + { + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) + { + T[] expectedVals = new T[buffer.Length()]; + + for (int i = 0; i < buffer.Length(); i++) + { + Span span = buffer.GetSpan(); + expectedVals[i] = getExpectedValue(i); + span[i] = expectedVals[i]; + } + + for (int i = 0; i < buffer.Length(); i++) + { + Span span = buffer.GetSpan(); + Assert.Equal(expectedVals[i], span[i]); + } + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void IndexingSpan_WhenOutOfRange_Throws_byte(int desiredLength) + { + this.TestIndexOutOfRangeShouldThrow(desiredLength, false); + this.TestIndexOutOfRangeShouldThrow(desiredLength, true); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void IndexingSpan_WhenOutOfRange_Throws_long(int desiredLength) + { + this.TestIndexOutOfRangeShouldThrow(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void IndexingSpan_WhenOutOfRange_Throws_CustomStruct(int desiredLength) + { + this.TestIndexOutOfRangeShouldThrow(desiredLength); + } + + private T TestIndexOutOfRangeShouldThrow(int desiredLength, bool testManagedByteBuffer = false) + where T : struct, IEquatable + { + var dummy = default(T); + + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) + { + Assert.ThrowsAny( + () => + { + Span span = buffer.GetSpan(); + dummy = span[desiredLength]; + }); + + Assert.ThrowsAny( + () => + { + Span span = buffer.GetSpan(); + dummy = span[desiredLength + 1]; + }); + + Assert.ThrowsAny( + () => + { + Span span = buffer.GetSpan(); + dummy = span[desiredLength + 42]; + }); + } + + return dummy; + } + + [Theory] + [InlineData(1)] + [InlineData(7)] + [InlineData(1024)] + [InlineData(6666)] + public void ManagedByteBuffer_ArrayIsCorrect(int desiredLength) + { + using (IManagedByteBuffer buffer = this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength)) + { + ref byte array0 = ref buffer.Array[0]; + ref byte span0 = ref buffer.GetReference(); + + Assert.True(Unsafe.AreSame(ref span0, ref array0)); + Assert.True(buffer.Array.Length >= buffer.GetSpan().Length); + } + } + + [Fact] + public void GetMemory_ReturnsValidMemory() + { + using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(42)) + { + Span span0 = buffer.GetSpan(); + span0[10].A = 30; + Memory memory = buffer.Memory; + + Assert.Equal(42, memory.Length); + Span span1 = memory.Span; + + Assert.Equal(42, span1.Length); + Assert.Equal(30, span1[10].A); + } + } + + [Fact] + public unsafe void GetMemory_ResultIsPinnable() + { + using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(42)) + { + Span span0 = buffer.GetSpan(); + span0[10] = 30; + + Memory memory = buffer.Memory; + + using (MemoryHandle h = memory.Pin()) + { + int* ptr = (int*)h.Pointer; + Assert.Equal(30, ptr[10]); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/Alocators/SimpleGcMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Alocators/SimpleGcMemoryAllocatorTests.cs new file mode 100644 index 0000000000..8e3b82be5b --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/Alocators/SimpleGcMemoryAllocatorTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.InteropServices; +using Xunit; + +namespace SixLabors.ImageSharp.Memory.Tests +{ + public class SimpleGcMemoryAllocatorTests + { + public class BufferTests : BufferTestSuite + { + public BufferTests() + : base(new SimpleGcMemoryAllocator()) + { + } + } + + protected SimpleGcMemoryAllocator MemoryAllocator { get; } = new SimpleGcMemoryAllocator(); + + [Theory] + [InlineData(-1)] + public void Allocate_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) + { + ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.Allocate(length)); + Assert.Equal("length", ex.ParamName); + } + + [Theory] + [InlineData(-1)] + public void AllocateManagedByteBuffer_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) + { + ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.AllocateManagedByteBuffer(length)); + Assert.Equal("length", ex.ParamName); + } + + [StructLayout(LayoutKind.Explicit, Size = 512)] + private struct BigStruct + { + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index ee32be3ca7..3b296f926c 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -7,8 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; -using SixLabors.Primitives; using Xunit; @@ -42,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Memory.Length); + Assert.Equal(width * height, buffer.GetMemory().Length); } } @@ -75,22 +73,6 @@ namespace SixLabors.ImageSharp.Tests.Memory } } - [Theory] - [InlineData(7, 42, 0, 0)] - [InlineData(7, 42, 3, 10)] - [InlineData(17, 42, 0, 41)] - public void GetRowSpanXY(int width, int height, int x, int y) - { - using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(width, height)) - { - Span span = buffer.GetRowSpan(x, y); - - // Assert.Equal(width * y + x, span.Start); - Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y + x); - } - } - [Theory] [InlineData(42, 8, 0, 0)] [InlineData(400, 1000, 20, 10)] @@ -140,7 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) { - rnd.RandomFill(b.Span, 0, 1); + rnd.RandomFill(b.GetSpan(), 0, 1); b.CopyColumns(startIndex, destIndex, columnCount); @@ -162,7 +144,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(100, 100)) { - rnd.RandomFill(b.Span, 0, 1); + rnd.RandomFill(b.GetSpan(), 0, 1); b.CopyColumns(0, 50, 22); b.CopyColumns(0, 50, 22); diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 9192798628..7c93128b48 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; using SixLabors.ImageSharp.Memory; -using SixLabors.Primitives; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Memory/MemorySourceTests.cs b/tests/ImageSharp.Tests/Memory/MemorySourceTests.cs index 535204e8dc..d940aa9874 100644 --- a/tests/ImageSharp.Tests/Memory/MemorySourceTests.cs +++ b/tests/ImageSharp.Tests/Memory/MemorySourceTests.cs @@ -5,7 +5,6 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs deleted file mode 100644 index bafb117f75..0000000000 --- a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Metadata; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - /// - /// Tests the class. - /// - public class ImageFrameMetaDataTests - { - [Fact] - public void ConstructorImageFrameMetaData() - { - const int frameDelay = 42; - const int colorTableLength = 128; - const GifDisposalMethod disposalMethod = GifDisposalMethod.RestoreToBackground; - - var metaData = new ImageFrameMetadata(); - GifFrameMetadata gifFrameMetaData = metaData.GetFormatMetadata(GifFormat.Instance); - gifFrameMetaData.FrameDelay = frameDelay; - gifFrameMetaData.ColorTableLength = colorTableLength; - gifFrameMetaData.DisposalMethod = disposalMethod; - - var clone = new ImageFrameMetadata(metaData); - GifFrameMetadata cloneGifFrameMetaData = clone.GetFormatMetadata(GifFormat.Instance); - - Assert.Equal(frameDelay, cloneGifFrameMetaData.FrameDelay); - Assert.Equal(colorTableLength, cloneGifFrameMetaData.ColorTableLength); - Assert.Equal(disposalMethod, cloneGifFrameMetaData.DisposalMethod); - } - - [Fact] - public void CloneIsDeep() - { - var metaData = new ImageFrameMetadata(); - ImageFrameMetadata clone = metaData.DeepClone(); - Assert.False(metaData.GetFormatMetadata(GifFormat.Instance).Equals(clone.GetFormatMetadata(GifFormat.Instance))); - } - } -} diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs deleted file mode 100644 index 8d786811cf..0000000000 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -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() - { - ExifProfile profile; - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image()) - { - profile = image.Metadata.ExifProfile; - } - - Assert.NotNull(profile); - - return profile.Values.First(); - } - - [Fact] - public void IEquatable() - { - ExifValue first = GetExifValue(); - ExifValue second = GetExifValue(); - - Assert.True(first == second); - Assert.True(first.Equals(second)); - Assert.True(first.Equals((object)second)); - } - - [Fact] - public void Properties() - { - ExifValue value = GetExifValue(); - - Assert.Equal(ExifDataType.Ascii, value.DataType); - Assert.Equal(ExifTag.GPSDOP, value.Tag); - Assert.False(value.IsArray); - Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString()); - Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.Value); - } - } -} diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs new file mode 100644 index 0000000000..746c0f3c72 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetadataTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Metadata; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Tests the class. + /// + public class ImageFrameMetadataTests + { + [Fact] + public void ConstructorImageFrameMetadata() + { + const int frameDelay = 42; + const int colorTableLength = 128; + const GifDisposalMethod disposalMethod = GifDisposalMethod.RestoreToBackground; + + var metaData = new ImageFrameMetadata(); + GifFrameMetadata gifFrameMetadata = metaData.GetGifMetadata(); + gifFrameMetadata.FrameDelay = frameDelay; + gifFrameMetadata.ColorTableLength = colorTableLength; + gifFrameMetadata.DisposalMethod = disposalMethod; + + var clone = new ImageFrameMetadata(metaData); + GifFrameMetadata cloneGifFrameMetadata = clone.GetGifMetadata(); + + Assert.Equal(frameDelay, cloneGifFrameMetadata.FrameDelay); + Assert.Equal(colorTableLength, cloneGifFrameMetadata.ColorTableLength); + Assert.Equal(disposalMethod, cloneGifFrameMetadata.DisposalMethod); + } + + [Fact] + public void CloneIsDeep() + { + var metaData = new ImageFrameMetadata(); + ImageFrameMetadata clone = metaData.DeepClone(); + Assert.False(metaData.GetGifMetadata().Equals(clone.GetGifMetadata())); + } + } +} diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs similarity index 95% rename from tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs rename to tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs index 39135d0037..60d791e91e 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs @@ -4,19 +4,18 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using Xunit; -namespace SixLabors.ImageSharp.Tests.MetaData +namespace SixLabors.ImageSharp.Tests.Metadata { /// /// Tests the class. /// - public class ImageMetaDataTests + public class ImageMetadataTests { [Fact] - public void ConstructorImageMetaData() + public void ConstructorImageMetadata() { var metaData = new ImageMetadata(); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs similarity index 62% rename from tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 4f928e0706..d2448f3d25 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -10,7 +9,6 @@ using System.Text; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using Xunit; @@ -44,16 +42,19 @@ namespace SixLabors.ImageSharp.Tests Assert.Null(image.Metadata.ExifProfile); + const string expected = "Dirk Lemstra"; image.Metadata.ExifProfile = new ExifProfile(); - image.Metadata.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra"); + image.Metadata.ExifProfile.SetValue(ExifTag.Copyright, expected); image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(1, image.Metadata.ExifProfile.Values.Count()); + Assert.Equal(1, image.Metadata.ExifProfile.Values.Count); - ExifValue value = image.Metadata.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); - TestValue(value, "Dirk Lemstra"); + IExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Copyright); + + Assert.NotNull(value); + Assert.Equal(expected, value.Value); } [Fact] @@ -100,9 +101,9 @@ namespace SixLabors.ImageSharp.Tests profile = image.Metadata.ExifProfile; Assert.NotNull(profile); - ExifValue value = profile.GetValue(ExifTag.ExposureTime); + IExifValue value = profile.GetValue(ExifTag.ExposureTime); Assert.NotNull(value); - Assert.NotEqual(exposureTime, ((Rational)value.Value).ToDouble()); + Assert.NotEqual(exposureTime, value.Value.ToDouble()); memStream.Position = 0; profile = GetExifProfile(); @@ -116,7 +117,9 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(profile); value = profile.GetValue(ExifTag.ExposureTime); - Assert.Equal(exposureTime, ((Rational)value.Value).ToDouble()); + Assert.Equal(exposureTime, value.Value.ToDouble()); + + image.Dispose(); } } @@ -129,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests image.Metadata.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndReadJpeg(image); - ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.ExposureBiasValue); + IExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.ExposureBiasValue); Assert.NotNull(value); Assert.Equal(new SignedRational(double.PositiveInfinity), value.Value); @@ -143,9 +146,9 @@ namespace SixLabors.ImageSharp.Tests image.Metadata.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity)); image = WriteAndRead(image, imageFormat); - value = image.Metadata.ExifProfile.GetValue(ExifTag.FlashEnergy); - Assert.NotNull(value); - Assert.Equal(new Rational(double.PositiveInfinity), value.Value); + IExifValue value2 = image.Metadata.ExifProfile.GetValue(ExifTag.FlashEnergy); + Assert.NotNull(value2); + Assert.Equal(new Rational(double.PositiveInfinity), value2.Value); } [Theory] @@ -153,77 +156,84 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void SetValue(TestImageWriteFormat imageFormat) { - var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); - ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); - TestValue(value, "ImageSharp"); + IExifValue software = image.Metadata.ExifProfile.GetValue(ExifTag.Software); + Assert.Equal("ImageSharp", software.Value); - Assert.Throws(() => { value.WithValue(15); }); + // ExifString can set integer values. + Assert.True(software.TrySetValue(15)); + Assert.False(software.TrySetValue(15F)); image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - - TestValue(value, new SignedRational(7555, 100)); + IExifValue shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - Assert.Throws(() => { value.WithValue(75); }); + Assert.Equal(new SignedRational(7555, 100), shutterSpeed.Value); + Assert.False(shutterSpeed.TrySetValue(75)); image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); // We also need to change this value because this overrides XResolution when the image is written. image.Metadata.HorizontalResolution = 150.0; - value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); - TestValue(value, new Rational(150, 1)); + IExifValue xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); + Assert.Equal(new Rational(150, 1), xResolution.Value); - Assert.Throws(() => { value.WithValue("ImageSharp"); }); + Assert.False(xResolution.TrySetValue("ImageSharp")); image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); - TestValue(value, (string)null); + IExifValue referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + Assert.Null(referenceBlackWhite.Value); - image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude); + var expectedLatitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; + image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, expectedLatitude); - value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); - TestValue(value, latitude); + IExifValue latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); + Assert.Equal(expectedLatitude, latitude.Value); + int profileCount = image.Metadata.ExifProfile.Values.Count; image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(17, image.Metadata.ExifProfile.Values.Count()); - value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); - TestValue(value, "ImageSharp"); + // Should be 3 less. + // 1 x due to setting of null "ReferenceBlackWhite" value. + // 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere + // strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2) + // https://exiftool.org/TagNames/EXIF.html + Assert.Equal(profileCount - 3, image.Metadata.ExifProfile.Values.Count); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - TestValue(value, new SignedRational(75.55)); + software = image.Metadata.ExifProfile.GetValue(ExifTag.Software); + Assert.Equal("15", software.Value); - value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); - TestValue(value, new Rational(150.0)); + shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); + Assert.Equal(new SignedRational(75.55), shutterSpeed.Value); - value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); - Assert.Null(value); + xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution); + Assert.Equal(new Rational(150.0), xResolution.Value); - value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); - TestValue(value, latitude); + referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + Assert.Null(referenceBlackWhite); + + latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude); + Assert.Equal(expectedLatitude, latitude.Value); image.Metadata.ExifProfile.Parts = ExifParts.ExifTags; image = WriteAndRead(image, imageFormat); Assert.NotNull(image.Metadata.ExifProfile); - Assert.Equal(8, image.Metadata.ExifProfile.Values.Count()); + Assert.Equal(8, image.Metadata.ExifProfile.Values.Count); Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace)); Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace)); - Assert.Equal(7, image.Metadata.ExifProfile.Values.Count()); + Assert.Equal(7, image.Metadata.ExifProfile.Values.Count); } [Fact] @@ -242,23 +252,23 @@ namespace SixLabors.ImageSharp.Tests metaData.HorizontalResolution = 100; - Assert.Equal(200, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(200, metaData.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble()); + Assert.Equal(300, metaData.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble()); exifProfile.Sync(metaData); - Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(100, (metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, (metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); metaData.VerticalResolution = 150; - Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(100, (metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, (metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); exifProfile.Sync(metaData); - Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(150, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(100, (metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(150, (metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); } [Fact] @@ -274,36 +284,42 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(170, thumbnail.Height); } - [Theory] - [InlineData(ExifTag.Software)] - [InlineData(ExifTag.Copyright)] - [InlineData(ExifTag.Model)] - [InlineData(ExifTag.ImageDescription)] - public void ReadWriteLargeProfileJpg(ExifTag exifValueToChange) + [Fact] + public void ReadWriteLargeProfileJpg() { - // arrange - var junk = new StringBuilder(); - for (int i = 0; i < 65600; i++) + ExifTag[] tags = new[] { ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription }; + foreach (ExifTag tag in tags) { - junk.Append("a"); - } - var image = new Image(100, 100); - ExifProfile expectedProfile = CreateExifProfile(); - var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); - expectedProfile.SetValue(exifValueToChange, junk.ToString()); - image.Metadata.ExifProfile = expectedProfile; + // Arrange + var junk = new StringBuilder(); + for (int i = 0; i < 65600; i++) + { + junk.Append("a"); + } - // act - Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); + var image = new Image(100, 100); + ExifProfile expectedProfile = CreateExifProfile(); + var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); + expectedProfile.SetValue(tag, junk.ToString()); + image.Metadata.ExifProfile = expectedProfile; - // assert - ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; - Assert.NotNull(actualProfile); - foreach (ExifTag expectedProfileTag in expectedProfileTags) - { - ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag); - ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag); - Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value); + // Act + Image reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); + + // Assert + ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; + Assert.NotNull(actualProfile); + + foreach (ExifTag expectedProfileTag in expectedProfileTags) + { + IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag); + IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag); + Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue()); + } + + IExifValue expected = expectedProfile.GetValue(tag); + IExifValue actual = actualProfile.GetValue(tag); + Assert.Equal(expected, actual); } } @@ -312,7 +328,6 @@ namespace SixLabors.ImageSharp.Tests { // This image contains an 802 byte EXIF profile // It has a tag with an index offset of 18,481,152 bytes (overrunning the data) - Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); Assert.NotNull(image); @@ -323,7 +338,8 @@ namespace SixLabors.ImageSharp.Tests { if (value.DataType == ExifDataType.Undefined) { - Assert.Equal(4, value.NumberOfComponents); + Assert.True(value.IsArray); + Assert.Equal(4U, 4 * ExifDataTypes.GetSize(value.DataType)); } } } @@ -338,10 +354,10 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(profile); // Force parsing of the profile. - Assert.Equal(24, profile.Values.Count); + Assert.Equal(25, profile.Values.Count); byte[] bytes = profile.ToByteArray(); - Assert.Equal(489, bytes.Length); + Assert.Equal(525, bytes.Length); } [Theory] @@ -349,46 +365,45 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat) { - // arrange + // Arrange var image = new Image(1, 1); - ExifProfile expected = CreateExifProfile(); - image.Metadata.ExifProfile = expected; + image.Metadata.ExifProfile = CreateExifProfile(); - // act + // Act Image reloadedImage = WriteAndRead(image, imageFormat); - // assert + // Assert ExifProfile actual = reloadedImage.Metadata.ExifProfile; Assert.NotNull(actual); foreach (KeyValuePair expectedProfileValue in TestProfileValues) { - ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key); + IExifValue actualProfileValue = actual.GetValueInternal(expectedProfileValue.Key); Assert.NotNull(actualProfileValue); - Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value); + Assert.Equal(expectedProfileValue.Value, actualProfileValue.GetValue()); } } [Fact] public void ProfileToByteArray() { - // arrange + // Arrange byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker; ExifProfile expectedProfile = CreateExifProfile(); var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList(); - // act + // Act byte[] actualBytes = expectedProfile.ToByteArray(); var actualProfile = new ExifProfile(actualBytes); - // assert + // Assert Assert.NotNull(actualBytes); Assert.NotEmpty(actualBytes); Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray()); foreach (ExifTag expectedProfileTag in expectedProfileTags) { - ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag); - ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag); - Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value); + IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag); + IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag); + Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue()); } } @@ -398,7 +413,7 @@ namespace SixLabors.ImageSharp.Tests foreach (KeyValuePair exifProfileValue in TestProfileValues) { - profile.SetValue(exifProfileValue.Key, exifProfileValue.Value); + profile.SetValueInternal(exifProfileValue.Key, exifProfileValue.Value); } return profile; @@ -423,7 +438,7 @@ namespace SixLabors.ImageSharp.Tests case TestImageWriteFormat.Png: return WriteAndReadPng(image); default: - throw new ArgumentException("unexpected test image format, only Jpeg and Png are allowed"); + throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"); } } @@ -455,65 +470,21 @@ namespace SixLabors.ImageSharp.Tests { Assert.NotNull(profile); - Assert.Equal(16, profile.Values.Count()); + Assert.Equal(16, profile.Values.Count); - foreach (ExifValue value in profile.Values) + foreach (IExifValue value in profile.Values) { - Assert.NotNull(value.Value); - - if (value.Tag == ExifTag.Software) - { - Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString()); - } - - if (value.Tag == ExifTag.XResolution) - { - Assert.Equal(new Rational(300.0), value.Value); - } - - if (value.Tag == ExifTag.PixelXDimension) - { - Assert.Equal(2338U, value.Value); - } + Assert.NotNull(value.GetValue()); } - } - - private static void TestValue(ExifValue value, string expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - - private static void TestValue(ExifValue value, Rational expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - - private static void TestValue(ExifValue value, SignedRational expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - private static void TestValue(ExifValue value, Rational[] expected) - { - Assert.NotNull(value); - - Assert.Equal(expected, (ICollection)value.Value); - } + IExifValue software = profile.GetValue(ExifTag.Software); + Assert.Equal("Windows Photo Editor 10.0.10011.16384", software.Value); - private static void TestValue(ExifValue value, double expected) - { - Assert.NotNull(value); - Assert.Equal(expected, value.Value); - } - - private static void TestValue(ExifValue value, double[] expected) - { - Assert.NotNull(value); + IExifValue xResolution = profile.GetValue(ExifTag.XResolution); + Assert.Equal(new Rational(300.0), xResolution.Value); - Assert.Equal(expected, (ICollection)value.Value); + IExifValue xDimension = profile.GetValue(ExifTag.PixelXDimension); + Assert.Equal(2338U, xDimension.Value); } } } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs similarity index 71% rename from tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs index 19ff7d269a..85c9231fa9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifReaderTests.cs @@ -1,6 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; @@ -12,9 +13,9 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Read_DataIsEmpty_ReturnsEmptyCollection() { - var reader = new ExifReader(new byte[] { }); + var reader = new ExifReader(Array.Empty()); - IList result = reader.ReadValues(); + IList result = reader.ReadValues(); Assert.Equal(0, result.Count); } @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests { var reader = new ExifReader(new byte[] { 69, 120, 105, 102, 0, 0 }); - IList result = reader.ReadValues(); + IList result = reader.ReadValues(); Assert.Equal(0, result.Count); } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs similarity index 81% rename from tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 144a6e4a33..a6ad8df8b4 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void TestExifTag() { - ExifProfile exifProfile = new ExifProfile(); + var exifProfile = new ExifProfile(); exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)1); - ExifValue value = exifProfile.GetValue(ExifTag.ResolutionUnit); + IExifValue value = exifProfile.GetValue(ExifTag.ResolutionUnit); Assert.Equal("None", value.ToString()); exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)2); @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests value = exifProfile.GetValue(ExifTag.ResolutionUnit); Assert.Equal("4", value.ToString()); - exifProfile.SetValue(ExifTag.ImageWidth, 123); + exifProfile.SetValue(ExifTag.ImageWidth, 123U); value = exifProfile.GetValue(ExifTag.ImageWidth); Assert.Equal("123", value.ToString()); } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs new file mode 100644 index 0000000000..7f52fb6cae --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs @@ -0,0 +1,53 @@ +// 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 +{ + public class ExifValueTests + { + private ExifProfile profile; + + public ExifValueTests() + { + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image()) + { + this.profile = image.Metadata.ExifProfile; + } + } + + private IExifValue GetExifValue() + { + Assert.NotNull(this.profile); + + return this.profile.GetValue(ExifTag.Software); + } + + [Fact] + public void IEquatable() + { + IExifValue first = this.GetExifValue(); + IExifValue second = this.GetExifValue(); + + Assert.True(first == second); + Assert.True(first.Equals(second)); + } + + [Fact] + public void Properties() + { + IExifValue value = this.GetExifValue(); + + Assert.Equal(ExifDataType.Ascii, value.DataType); + Assert.Equal(ExifTag.Software, value.Tag); + Assert.False(value.IsArray); + + const string expected = "Windows Photo Editor 10.0.10011.16384"; + Assert.Equal(expected, value.ToString()); + Assert.Equal(expected, value.Value); + } + } +} diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs new file mode 100644 index 0000000000..231ad8f0d6 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -0,0 +1,552 @@ +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values +{ + public class ExifValuesTests + { + public static TheoryData ByteTags => new TheoryData + { + { ExifTag.FaxProfile }, + { ExifTag.ModeNumber }, + { ExifTag.GPSAltitudeRef } + }; + + public static TheoryData ByteArrayTags => new TheoryData + { + { ExifTag.ClipPath }, + { ExifTag.VersionYear }, + { ExifTag.XMP }, + { ExifTag.CFAPattern2 }, + { ExifTag.TIFFEPStandardID }, + { ExifTag.XPTitle }, + { ExifTag.XPComment }, + { ExifTag.XPAuthor }, + { ExifTag.XPKeywords }, + { ExifTag.XPSubject }, + { ExifTag.GPSVersionID }, + }; + + public static TheoryData DoubleArrayTags => new TheoryData + { + { ExifTag.PixelScale }, + { ExifTag.IntergraphMatrix }, + { ExifTag.ModelTiePoint }, + { ExifTag.ModelTransform } + }; + + public static TheoryData LongTags => new TheoryData + { + { ExifTag.SubfileType }, + { ExifTag.SubIFDOffset }, + { ExifTag.GPSIFDOffset }, + { ExifTag.T4Options }, + { ExifTag.T6Options }, + { ExifTag.XClipPathUnits }, + { ExifTag.YClipPathUnits }, + { ExifTag.ProfileType }, + { ExifTag.CodingMethods }, + { ExifTag.T82ptions }, + { ExifTag.JPEGInterchangeFormat }, + { ExifTag.JPEGInterchangeFormatLength }, + { ExifTag.MDFileTag }, + { ExifTag.StandardOutputSensitivity }, + { ExifTag.RecommendedExposureIndex }, + { ExifTag.ISOSpeed }, + { ExifTag.ISOSpeedLatitudeyyy }, + { ExifTag.ISOSpeedLatitudezzz }, + { ExifTag.FaxRecvParams }, + { ExifTag.FaxRecvTime }, + { ExifTag.ImageNumber }, + }; + + public static TheoryData LongArrayTags => new TheoryData + { + { ExifTag.FreeOffsets }, + { ExifTag.FreeByteCounts }, + { ExifTag.ColorResponseUnit }, + { ExifTag.TileOffsets }, + { ExifTag.SMinSampleValue }, + { ExifTag.SMaxSampleValue }, + { ExifTag.JPEGQTables }, + { ExifTag.JPEGDCTables }, + { ExifTag.JPEGACTables }, + { ExifTag.StripRowCounts }, + { ExifTag.IntergraphRegisters }, + { ExifTag.TimeZoneOffset } + }; + + public static TheoryData NumberTags => new TheoryData + { + { ExifTag.ImageWidth }, + { ExifTag.ImageLength }, + { ExifTag.TileWidth }, + { ExifTag.TileLength }, + { ExifTag.BadFaxLines }, + { ExifTag.ConsecutiveBadFaxLines }, + { ExifTag.PixelXDimension }, + { ExifTag.PixelYDimension } + }; + + public static TheoryData NumberArrayTags => new TheoryData + { + { ExifTag.StripOffsets }, + { ExifTag.TileByteCounts }, + { ExifTag.ImageLayer } + }; + + public static TheoryData RationalTags => new TheoryData + { + { ExifTag.XPosition }, + { ExifTag.YPosition }, + { ExifTag.XResolution }, + { ExifTag.YResolution }, + { ExifTag.BatteryLevel }, + { ExifTag.ExposureTime }, + { ExifTag.FNumber }, + { ExifTag.MDScalePixel }, + { ExifTag.CompressedBitsPerPixel }, + { ExifTag.ApertureValue }, + { ExifTag.MaxApertureValue }, + { ExifTag.SubjectDistance }, + { ExifTag.FocalLength }, + { ExifTag.FlashEnergy2 }, + { ExifTag.FocalPlaneXResolution2 }, + { ExifTag.FocalPlaneYResolution2 }, + { ExifTag.ExposureIndex2 }, + { ExifTag.Humidity }, + { ExifTag.Pressure }, + { ExifTag.Acceleration }, + { ExifTag.FlashEnergy }, + { ExifTag.FocalPlaneXResolution }, + { ExifTag.FocalPlaneYResolution }, + { ExifTag.ExposureIndex }, + { ExifTag.DigitalZoomRatio }, + { ExifTag.GPSAltitude }, + { ExifTag.GPSDOP }, + { ExifTag.GPSSpeed }, + { ExifTag.GPSTrack }, + { ExifTag.GPSImgDirection }, + { ExifTag.GPSDestBearing }, + { ExifTag.GPSDestDistance }, + }; + + public static TheoryData RationalArrayTags => new TheoryData + { + { ExifTag.WhitePoint }, + { ExifTag.PrimaryChromaticities }, + { ExifTag.YCbCrCoefficients }, + { ExifTag.ReferenceBlackWhite }, + { ExifTag.GPSLatitude }, + { ExifTag.GPSLongitude }, + { ExifTag.GPSTimestamp }, + { ExifTag.GPSDestLatitude }, + { ExifTag.GPSDestLongitude }, + { ExifTag.LensSpecification } + }; + + public static TheoryData ShortTags => new TheoryData + { + { ExifTag.OldSubfileType }, + { ExifTag.Compression }, + { ExifTag.PhotometricInterpretation }, + { ExifTag.Thresholding }, + { ExifTag.CellWidth }, + { ExifTag.CellLength }, + { ExifTag.FillOrder }, + { ExifTag.Orientation }, + { ExifTag.SamplesPerPixel }, + { ExifTag.PlanarConfiguration }, + { ExifTag.GrayResponseUnit }, + { ExifTag.ResolutionUnit }, + { ExifTag.CleanFaxData }, + { ExifTag.InkSet }, + { ExifTag.NumberOfInks }, + { ExifTag.DotRange }, + { ExifTag.Indexed }, + { ExifTag.OPIProxy }, + { ExifTag.JPEGProc }, + { ExifTag.JPEGRestartInterval }, + { ExifTag.YCbCrPositioning }, + { ExifTag.Rating }, + { ExifTag.RatingPercent }, + { ExifTag.ExposureProgram }, + { ExifTag.Interlace }, + { ExifTag.SelfTimerMode }, + { ExifTag.SensitivityType }, + { ExifTag.MeteringMode }, + { ExifTag.LightSource }, + { ExifTag.FocalPlaneResolutionUnit2 }, + { ExifTag.SensingMethod2 }, + { ExifTag.Flash }, + { ExifTag.ColorSpace }, + { ExifTag.FocalPlaneResolutionUnit }, + { ExifTag.SensingMethod }, + { ExifTag.CustomRendered }, + { ExifTag.ExposureMode }, + { ExifTag.WhiteBalance }, + { ExifTag.FocalLengthIn35mmFilm }, + { ExifTag.SceneCaptureType }, + { ExifTag.GainControl }, + { ExifTag.Contrast }, + { ExifTag.Saturation }, + { ExifTag.Sharpness }, + { ExifTag.SubjectDistanceRange }, + { ExifTag.GPSDifferential } + }; + + public static TheoryData ShortArrayTags => new TheoryData + { + { ExifTag.BitsPerSample }, + { ExifTag.MinSampleValue }, + { ExifTag.MaxSampleValue }, + { ExifTag.GrayResponseCurve }, + { ExifTag.ColorMap }, + { ExifTag.ExtraSamples }, + { ExifTag.PageNumber }, + { ExifTag.TransferFunction }, + { ExifTag.Predictor }, + { ExifTag.HalftoneHints }, + { ExifTag.SampleFormat }, + { ExifTag.TransferRange }, + { ExifTag.DefaultImageColor }, + { ExifTag.JPEGLosslessPredictors }, + { ExifTag.JPEGPointTransforms }, + { ExifTag.YCbCrSubsampling }, + { ExifTag.CFARepeatPatternDim }, + { ExifTag.IntergraphPacketData }, + { ExifTag.ISOSpeedRatings }, + { ExifTag.SubjectArea }, + { ExifTag.SubjectLocation } + }; + + public static TheoryData SignedRationalTags => new TheoryData + { + { ExifTag.ShutterSpeedValue }, + { ExifTag.BrightnessValue }, + { ExifTag.ExposureBiasValue }, + { ExifTag.AmbientTemperature }, + { ExifTag.WaterDepth }, + { ExifTag.CameraElevationAngle } + }; + + public static TheoryData SignedRationalArrayTags => new TheoryData + { + { ExifTag.Decode } + }; + + public static TheoryData StringTags => new TheoryData + { + { ExifTag.ImageDescription }, + { ExifTag.Make }, + { ExifTag.Model }, + { ExifTag.Software }, + { ExifTag.DateTime }, + { ExifTag.Artist }, + { ExifTag.HostComputer }, + { ExifTag.Copyright }, + { ExifTag.DocumentName }, + { ExifTag.PageName }, + { ExifTag.InkNames }, + { ExifTag.TargetPrinter }, + { ExifTag.ImageID }, + { ExifTag.MDLabName }, + { ExifTag.MDSampleInfo }, + { ExifTag.MDPrepDate }, + { ExifTag.MDPrepTime }, + { ExifTag.MDFileUnits }, + { ExifTag.SEMInfo }, + { ExifTag.SpectralSensitivity }, + { ExifTag.DateTimeOriginal }, + { ExifTag.DateTimeDigitized }, + { ExifTag.SubsecTime }, + { ExifTag.SubsecTimeOriginal }, + { ExifTag.SubsecTimeDigitized }, + { ExifTag.RelatedSoundFile }, + { ExifTag.FaxSubaddress }, + { ExifTag.OffsetTime }, + { ExifTag.OffsetTimeOriginal }, + { ExifTag.OffsetTimeDigitized }, + { ExifTag.SecurityClassification }, + { ExifTag.ImageHistory }, + { ExifTag.ImageUniqueID }, + { ExifTag.OwnerName }, + { ExifTag.SerialNumber }, + { ExifTag.LensMake }, + { ExifTag.LensModel }, + { ExifTag.LensSerialNumber }, + { ExifTag.GDALMetadata }, + { ExifTag.GDALNoData }, + { ExifTag.GPSLatitudeRef }, + { ExifTag.GPSLongitudeRef }, + { ExifTag.GPSSatellites }, + { ExifTag.GPSStatus }, + { ExifTag.GPSMeasureMode }, + { ExifTag.GPSSpeedRef }, + { ExifTag.GPSTrackRef }, + { ExifTag.GPSImgDirectionRef }, + { ExifTag.GPSMapDatum }, + { ExifTag.GPSDestLatitudeRef }, + { ExifTag.GPSDestLongitudeRef }, + { ExifTag.GPSDestBearingRef }, + { ExifTag.GPSDestDistanceRef }, + { ExifTag.GPSDateStamp } + }; + + public static TheoryData UndefinedTags => new TheoryData + { + { ExifTag.FileSource }, + { ExifTag.SceneType } + }; + + public static TheoryData UndefinedArrayTags => new TheoryData + { + { ExifTag.JPEGTables }, + { ExifTag.OECF }, + { ExifTag.ExifVersion }, + { ExifTag.ComponentsConfiguration }, + { ExifTag.MakerNote }, + { ExifTag.UserComment }, + { ExifTag.FlashpixVersion }, + { ExifTag.SpatialFrequencyResponse }, + { ExifTag.SpatialFrequencyResponse2 }, + { ExifTag.Noise }, + { ExifTag.CFAPattern }, + { ExifTag.DeviceSettingDescription }, + { ExifTag.ImageSourceData }, + { ExifTag.GPSProcessingMethod }, + { ExifTag.GPSAreaInformation } + }; + + [Theory] + [MemberData(nameof(ByteTags))] + public void ExifByteTests(ExifTag tag) + { + const byte expected = byte.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByte)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(ByteArrayTags))] + public void ExifByteArrayTests(ExifTag tag) + { + byte[] expected = new[] { byte.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByteArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(DoubleArrayTags))] + public void ExifDoubleArrayTests(ExifTag tag) + { + double[] expected = new[] { double.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifDoubleArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(LongTags))] + public void ExifLongTests(ExifTag tag) + { + const uint expected = uint.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifLong)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(LongArrayTags))] + public void ExifLongArrayTests(ExifTag tag) + { + uint[] expected = new[] { uint.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifLongArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(NumberTags))] + public void ExifNumberTests(ExifTag tag) + { + Number expected = ushort.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((uint)expected)); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifNumber)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(NumberArrayTags))] + public void ExifNumberArrayTests(ExifTag tag) + { + Number[] expected = new[] { new Number(uint.MaxValue) }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifNumberArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(RationalTags))] + public void ExifRationalTests(ExifTag tag) + { + var expected = new Rational(21, 42); + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(new SignedRational(expected.ToDouble()))); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifRational)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(RationalArrayTags))] + public void ExifRationalArrayTests(ExifTag tag) + { + Rational[] expected = new[] { new Rational(21, 42) }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifRationalArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(ShortTags))] + public void ExifShortTests(ExifTag tag) + { + const ushort expected = (ushort)short.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue((short)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifShort)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(ShortArrayTags))] + public void ExifShortArrayTests(ExifTag tag) + { + ushort[] expected = new[] { ushort.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifShortArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(SignedRationalTags))] + public void ExifSignedRationalTests(ExifTag tag) + { + var expected = new SignedRational(21, 42); + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifSignedRational)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(SignedRationalArrayTags))] + public void ExifSignedRationalArrayTests(ExifTag tag) + { + SignedRational[] expected = new[] { new SignedRational(21, 42) }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifSignedRationalArray)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(StringTags))] + public void ExifStringTests(ExifTag tag) + { + const string expected = "ImageSharp"; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(0M)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifString)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(UndefinedTags))] + public void ExifUndefinedTests(ExifTag tag) + { + const byte expected = byte.MaxValue; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue((int)expected)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByte)value; + Assert.Equal(expected, typed.Value); + } + + [Theory] + [MemberData(nameof(UndefinedArrayTags))] + public void ExifUndefinedArrayTests(ExifTag tag) + { + byte[] expected = new[] { byte.MaxValue }; + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(expected.ToString())); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifByteArray)value; + Assert.Equal(expected, typed.Value); + } + } +} diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.LutTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.LutTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataReader/IccDataReaderTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs similarity index 98% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs index 15bf762e25..3667fda144 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs @@ -8,7 +8,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Icc { - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp; public class IccDataWriterMatrixTests { diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccProfileTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccReaderTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/IccWriterTests.cs diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/Various/IccProfileIdTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs similarity index 100% rename from tests/ImageSharp.Tests/MetaData/Profiles/ICC/Various/IccProfileIdTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/ICC/Various/IccProfileIdTests.cs diff --git a/tests/ImageSharp.Tests/Numerics/RationalTests.cs b/tests/ImageSharp.Tests/Numerics/RationalTests.cs index 7cdae7f602..7b3bd86fc9 100644 --- a/tests/ImageSharp.Tests/Numerics/RationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RationalTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs index f828cf74f5..2931ab3910 100644 --- a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; - using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs similarity index 61% rename from tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs rename to tests/ImageSharp.Tests/PixelFormats/A8Tests.cs index b6e87626b6..6b542badff 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/A8Tests.cs @@ -7,42 +7,42 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.PixelFormats { - public class Alpha8Tests + public class A8Tests { [Fact] - public void Alpha8_Constructor() + public void A8_Constructor() { // Test the limits. - Assert.Equal(byte.MinValue, new Alpha8(0F).PackedValue); - Assert.Equal(byte.MaxValue, new Alpha8(1F).PackedValue); + Assert.Equal(byte.MinValue, new A8(0F).PackedValue); + Assert.Equal(byte.MaxValue, new A8(1F).PackedValue); // Test clamping. - Assert.Equal(byte.MinValue, new Alpha8(-1234F).PackedValue); - Assert.Equal(byte.MaxValue, new Alpha8(1234F).PackedValue); + Assert.Equal(byte.MinValue, new A8(-1234F).PackedValue); + Assert.Equal(byte.MaxValue, new A8(1234F).PackedValue); // Test ordering - Assert.Equal(124, new Alpha8(124F / byte.MaxValue).PackedValue); - Assert.Equal(26, new Alpha8(0.1F).PackedValue); + Assert.Equal(124, new A8(124F / byte.MaxValue).PackedValue); + Assert.Equal(26, new A8(0.1F).PackedValue); } [Fact] - public void Alpha8_Equality() + public void A8_Equality() { - var left = new Alpha8(16); - var right = new Alpha8(32); + var left = new A8(16); + var right = new A8(32); - Assert.True(left == new Alpha8(16)); + Assert.True(left == new A8(16)); Assert.True(left != right); - Assert.Equal(left, (object)new Alpha8(16)); + Assert.Equal(left, (object)new A8(16)); } [Fact] - public void Alpha8_FromScaledVector4() + public void A8_FromScaledVector4() { // Arrange - Alpha8 alpha = default; + A8 alpha = default; int expected = 128; - Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); + Vector4 scaled = new A8(.5F).ToScaledVector4(); // Act alpha.FromScaledVector4(scaled); @@ -53,10 +53,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Alpha8_ToScaledVector4() + public void A8_ToScaledVector4() { // Arrange - var alpha = new Alpha8(.5F); + var alpha = new A8(.5F); // Act Vector4 actual = alpha.ToScaledVector4(); @@ -69,10 +69,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Alpha8_ToVector4() + public void A8_ToVector4() { // Arrange - var alpha = new Alpha8(.5F); + var alpha = new A8(.5F); // Act var actual = alpha.ToVector4(); @@ -85,9 +85,9 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Alpha8_ToRgba32() + public void A8_ToRgba32() { - var input = new Alpha8(128); + var input = new A8(128); var expected = new Rgba32(0, 0, 0, 128); Rgba32 actual = default; @@ -96,10 +96,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Alpha8_FromBgra5551() + public void A8_FromBgra5551() { // arrange - var alpha = default(Alpha8); + var alpha = default(A8); byte expected = byte.MaxValue; // act diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index ccefa85c1e..3043626cab 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ushort expected = ushort.MaxValue; // act - bgr.FromGray8(new Gray8(byte.MaxValue)); + bgr.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expected, bgr.PackedValue); @@ -235,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ushort expected = ushort.MaxValue; // act - bgr.FromGray16(new Gray16(ushort.MaxValue)); + bgr.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expected, bgr.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index d4c9986252..b979eebdec 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromGray16(new Gray16(ushort.MaxValue)); + bgra.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, bgra.PackedValue); @@ -204,7 +204,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromGray8(new Gray8(byte.MaxValue)); + bgra.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, bgra.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index 7751d7ab93..41ebfc9550 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromGray16(new Gray16(ushort.MaxValue)); + bgra.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, bgra.PackedValue); @@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ushort expectedPackedValue = ushort.MaxValue; // act - bgra.FromGray8(new Gray8(byte.MaxValue)); + bgra.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, bgra.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 2dff656ac8..2eb5553d7c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expectedPackedValue = uint.MaxValue; // act - byte4.FromGray8(new Gray8(byte.MaxValue)); + byte4.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, byte4.PackedValue); @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expectedPackedValue = uint.MaxValue; // act - byte4.FromGray16(new Gray16(ushort.MaxValue)); + byte4.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, byte4.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs similarity index 75% rename from tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs rename to tests/ImageSharp.Tests/PixelFormats/L16Tests.cs index 8a0bd62c19..179ba12b2d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L16Tests.cs @@ -7,13 +7,13 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.PixelFormats { - public class Gray16Tests + public class L16Tests { [Fact] public void AreEqual() { - var color1 = new Gray16(3000); - var color2 = new Gray16(3000); + var color1 = new L16(3000); + var color2 = new L16(3000); Assert.Equal(color1, color2); } @@ -21,8 +21,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void AreNotEqual() { - var color1 = new Gray16(12345); - var color2 = new Gray16(54321); + var color1 = new L16(12345); + var color2 = new L16(54321); Assert.NotEqual(color1, color2); } @@ -32,16 +32,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [InlineData(65535)] [InlineData(32767)] [InlineData(42)] - public void Gray16_PackedValue_EqualsInput(ushort input) - => Assert.Equal(input, new Gray16(input).PackedValue); + public void L16_PackedValue_EqualsInput(ushort input) + => Assert.Equal(input, new L16(input).PackedValue); [Fact] - public void Gray16_FromScaledVector4() + public void L16_FromScaledVector4() { // Arrange - Gray16 gray = default; + L16 gray = default; const ushort expected = 32767; - Vector4 scaled = new Gray16(expected).ToScaledVector4(); + Vector4 scaled = new L16(expected).ToScaledVector4(); // Act gray.FromScaledVector4(scaled); @@ -55,10 +55,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [InlineData(0)] [InlineData(65535)] [InlineData(32767)] - public void Gray16_ToScaledVector4(ushort input) + public void L16_ToScaledVector4(ushort input) { // Arrange - var gray = new Gray16(input); + var gray = new L16(input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -72,12 +72,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Gray16_FromVector4() + public void L16_FromVector4() { // Arrange - Gray16 gray = default; + L16 gray = default; const ushort expected = 32767; - var vector = new Gray16(expected).ToVector4(); + var vector = new L16(expected).ToVector4(); // Act gray.FromVector4(vector); @@ -91,10 +91,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [InlineData(0)] [InlineData(65535)] [InlineData(32767)] - public void Gray16_ToVector4(ushort input) + public void L16_ToVector4(ushort input) { // Arrange - var gray = new Gray16(input); + var gray = new L16(input); // Act var actual = gray.ToVector4(); @@ -108,10 +108,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Gray16_FromRgba32() + public void L16_FromRgba32() { // Arrange - Gray16 gray = default; + L16 gray = default; const byte rgb = 128; ushort scaledRgb = ImageMaths.UpscaleFrom8BitTo16Bit(rgb); ushort expected = ImageMaths.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); @@ -128,11 +128,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [InlineData(0)] [InlineData(65535)] [InlineData(8100)] - public void Gray16_ToRgba32(ushort input) + public void L16_ToRgba32(ushort input) { // Arrange ushort expected = ImageMaths.DownScaleFrom16BitTo8Bit(input); - var gray = new Gray16(input); + var gray = new L16(input); // Act Rgba32 actual = default; @@ -146,10 +146,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Gray16_FromBgra5551() + public void L16_FromBgra5551() { // arrange - var gray = default(Gray16); + var gray = default(L16); ushort expected = ushort.MaxValue; // act diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs similarity index 58% rename from tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs rename to tests/ImageSharp.Tests/PixelFormats/L8Tests.cs index d7b50ee1e2..13999ee3b2 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/L8Tests.cs @@ -9,65 +9,66 @@ using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.PixelFormats { - public class Gray8Tests + public class L8Tests { - public static readonly TheoryData LuminanceData = new TheoryData - { - 0, - 1, - 2, - 3, - 5, - 13, - 31, - 71, - 73, - 79, - 83, - 109, - 127, - 128, - 131, - 199, - 250, - 251, - 254, - 255 - }; + public static readonly TheoryData LuminanceData + = new TheoryData + { + 0, + 1, + 2, + 3, + 5, + 13, + 31, + 71, + 73, + 79, + 83, + 109, + 127, + 128, + 131, + 199, + 250, + 251, + 254, + 255 + }; [Theory] [InlineData(0)] [InlineData(255)] [InlineData(10)] [InlineData(42)] - public void Gray8_PackedValue_EqualsInput(byte input) - => Assert.Equal(input, new Gray8(input).PackedValue); + public void L8_PackedValue_EqualsInput(byte input) + => Assert.Equal(input, new L8(input).PackedValue); [Fact] public void AreEqual() { - var color1 = new Gray8(100); - var color2 = new Gray8(100); - + var color1 = new L8(100); + var color2 = new L8(100); + Assert.Equal(color1, color2); } [Fact] public void AreNotEqual() { - var color1 = new Gray8(100); - var color2 = new Gray8(200); + var color1 = new L8(100); + var color2 = new L8(200); Assert.NotEqual(color1, color2); } [Fact] - public void Gray8_FromScaledVector4() + public void L8_FromScaledVector4() { // Arrange - Gray8 gray = default; + L8 gray = default; const byte expected = 128; - Vector4 scaled = new Gray8(expected).ToScaledVector4(); + Vector4 scaled = new L8(expected).ToScaledVector4(); // Act gray.FromScaledVector4(scaled); @@ -79,10 +80,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Theory] [MemberData(nameof(LuminanceData))] - public void Gray8_ToScaledVector4(byte input) + public void L8_ToScaledVector4(byte input) { // Arrange - var gray = new Gray8(input); + var gray = new L8(input); // Act Vector4 actual = gray.ToScaledVector4(); @@ -97,11 +98,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Theory] [MemberData(nameof(LuminanceData))] - public void Gray8_FromVector4(byte luminance) + public void L8_FromVector4(byte luminance) { // Arrange - Gray8 gray = default; - var vector = new Gray8(luminance).ToVector4(); + L8 gray = default; + var vector = new L8(luminance).ToVector4(); // Act gray.FromVector4(vector); @@ -113,10 +114,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Theory] [MemberData(nameof(LuminanceData))] - public void Gray8_ToVector4(byte input) + public void L8_ToVector4(byte input) { // Arrange - var gray = new Gray8(input); + var gray = new L8(input); // Act var actual = gray.ToVector4(); @@ -131,10 +132,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Theory] [MemberData(nameof(LuminanceData))] - public void Gray8_FromRgba32(byte rgb) + public void L8_FromRgba32(byte rgb) { // Arrange - Gray8 gray = default; + L8 gray = default; byte expected = ImageMaths.Get8BitBT709Luminance(rgb, rgb, rgb); // Act @@ -145,13 +146,13 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(expected, actual); } - + [Theory] [MemberData(nameof(LuminanceData))] - public void Gray8_ToRgba32(byte luminance) + public void L8_ToRgba32(byte luminance) { // Arrange - var gray = new Gray8(luminance); + var gray = new L8(luminance); // Act Rgba32 actual = default; @@ -165,10 +166,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Gray8_FromBgra5551() + public void L8_FromBgra5551() { // arrange - var grey = default(Gray8); + var grey = default(L8); byte expected = byte.MaxValue; // act @@ -181,18 +182,18 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public class Rgba32Compatibility { // ReSharper disable once MemberHidesStaticFromOuterClass - public static readonly TheoryData LuminanceData = Gray8Tests.LuminanceData; + public static readonly TheoryData LuminanceData = L8Tests.LuminanceData; [Theory] [MemberData(nameof(LuminanceData))] - public void Gray8_FromRgba32_IsInverseOf_ToRgba32(byte luminance) + public void L8_FromRgba32_IsInverseOf_ToRgba32(byte luminance) { - var original = new Gray8(luminance); + var original = new L8(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - Gray8 mirror = default; + L8 mirror = default; mirror.FromRgba32(rgba); Assert.Equal(original, mirror); @@ -201,14 +202,14 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Theory] [MemberData(nameof(LuminanceData))] - public void Rgba32_ToGray8_IsInverseOf_Gray8_ToRgba32(byte luminance) + public void Rgba32_ToL8_IsInverseOf_L8_ToRgba32(byte luminance) { - var original = new Gray8(luminance); + var original = new L8(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - Gray8 mirror = default; + L8 mirror = default; mirror.FromRgba32(rgba); Assert.Equal(original, mirror); @@ -218,29 +219,29 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [MemberData(nameof(LuminanceData))] public void ToVector4_IsRgba32Compatible(byte luminance) { - var original = new Gray8(luminance); + var original = new L8(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - var gray8Vector = original.ToVector4(); + var L8Vector = original.ToVector4(); var rgbaVector = original.ToVector4(); - Assert.Equal(gray8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); + Assert.Equal(L8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } [Theory] [MemberData(nameof(LuminanceData))] public void FromVector4_IsRgba32Compatible(byte luminance) { - var original = new Gray8(luminance); + var original = new L8(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); Vector4 rgbaVector = original.ToVector4(); - Gray8 mirror = default; + L8 mirror = default; mirror.FromVector4(rgbaVector); Assert.Equal(original, mirror); @@ -250,29 +251,29 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [MemberData(nameof(LuminanceData))] public void ToScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new Gray8(luminance); + var original = new L8(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); - Vector4 gray8Vector = original.ToScaledVector4(); + Vector4 L8Vector = original.ToScaledVector4(); Vector4 rgbaVector = original.ToScaledVector4(); - Assert.Equal(gray8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); + Assert.Equal(L8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); } [Theory] [MemberData(nameof(LuminanceData))] public void FromScaledVector4_IsRgba32Compatible(byte luminance) { - var original = new Gray8(luminance); + var original = new L8(luminance); Rgba32 rgba = default; original.ToRgba32(ref rgba); Vector4 rgbaVector = original.ToScaledVector4(); - Gray8 mirror = default; + L8 mirror = default; mirror.FromScaledVector4(rgbaVector); Assert.Equal(original, mirror); diff --git a/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs new file mode 100644 index 0000000000..3663350067 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/La16Tests.cs @@ -0,0 +1,287 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public class La16Tests + { + public static readonly TheoryData LuminanceData + = new TheoryData + { + 0, + 1, + 2, + 3, + 5, + 13, + 31, + 71, + 73, + 79, + 83, + 109, + 127, + 128, + 131, + 199, + 250, + 251, + 254, + 255 + }; + + [Theory] + [InlineData(0, 0)] + [InlineData(255, 65535)] + [InlineData(10, 2570)] + [InlineData(42, 10794)] + public void La16_PackedValue_EqualsPackedInput(byte input, ushort packed) + => Assert.Equal(packed, new La16(input, input).PackedValue); + + [Fact] + public void AreEqual() + { + var color1 = new La16(100, 50); + var color2 = new La16(100, 50); + + Assert.Equal(color1, color2); + } + + [Fact] + public void AreNotEqual() + { + var color1 = new La16(100, 50); + var color2 = new La16(200, 50); + + Assert.NotEqual(color1, color2); + } + + [Fact] + public void La16_FromScaledVector4() + { + // Arrange + La16 gray = default; + const ushort expected = 32896; + Vector4 scaled = new La16(128, 128).ToScaledVector4(); + + // Act + gray.FromScaledVector4(scaled); + ushort actual = gray.PackedValue; + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void La16_ToScaledVector4(byte input) + { + // Arrange + var gray = new La16(input, input); + + // Act + Vector4 actual = gray.ToScaledVector4(); + + // Assert + float scaledInput = input / 255F; + Assert.Equal(scaledInput, actual.X); + Assert.Equal(scaledInput, actual.Y); + Assert.Equal(scaledInput, actual.Z); + Assert.Equal(scaledInput, actual.W); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void La16_FromVector4(byte luminance) + { + // Arrange + La16 gray = default; + var vector = new La16(luminance, luminance).ToVector4(); + + // Act + gray.FromVector4(vector); + byte actualL = gray.L; + byte actualA = gray.A; + + // Assert + Assert.Equal(luminance, actualL); + Assert.Equal(luminance, actualA); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void La16_ToVector4(byte input) + { + // Arrange + var gray = new La16(input, input); + + // Act + var actual = gray.ToVector4(); + + // Assert + float scaledInput = input / 255F; + Assert.Equal(scaledInput, actual.X); + Assert.Equal(scaledInput, actual.Y); + Assert.Equal(scaledInput, actual.Z); + Assert.Equal(scaledInput, actual.W); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void La16_FromRgba32(byte rgb) + { + // Arrange + La16 gray = default; + byte expected = ImageMaths.Get8BitBT709Luminance(rgb, rgb, rgb); + + // Act + gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); + byte actual = gray.L; + + // Assert + Assert.Equal(expected, actual); + Assert.Equal(255, gray.A); + } + + + [Theory] + [MemberData(nameof(LuminanceData))] + public void La16_ToRgba32(byte luminance) + { + // Arrange + var gray = new La16(luminance, luminance); + + // Act + Rgba32 actual = default; + gray.ToRgba32(ref actual); + + // Assert + Assert.Equal(luminance, actual.R); + Assert.Equal(luminance, actual.G); + Assert.Equal(luminance, actual.B); + Assert.Equal(luminance, actual.A); + } + + [Fact] + public void La16_FromBgra5551() + { + // arrange + var grey = default(La16); + byte expected = byte.MaxValue; + + // act + grey.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, grey.L); + Assert.Equal(expected, grey.A); + } + + public class Rgba32Compatibility + { + // ReSharper disable once MemberHidesStaticFromOuterClass + public static readonly TheoryData LuminanceData = La16Tests.LuminanceData; + + [Theory] + [MemberData(nameof(LuminanceData))] + public void La16_FromRgba32_IsInverseOf_ToRgba32(byte luminance) + { + var original = new La16(luminance, luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + La16 mirror = default; + mirror.FromRgba32(rgba); + + Assert.Equal(original, mirror); + } + + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Rgba32_ToLa16_IsInverseOf_La16_ToRgba32(byte luminance) + { + var original = new La16(luminance, luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + La16 mirror = default; + mirror.FromRgba32(rgba); + + Assert.Equal(original, mirror); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void ToVector4_IsRgba32Compatible(byte luminance) + { + var original = new La16(luminance, luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + var La16Vector = original.ToVector4(); + var rgbaVector = original.ToVector4(); + + Assert.Equal(La16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void FromVector4_IsRgba32Compatible(byte luminance) + { + var original = new La16(luminance, luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + var rgbaVector = original.ToVector4(); + + La16 mirror = default; + mirror.FromVector4(rgbaVector); + + Assert.Equal(original, mirror); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void ToScaledVector4_IsRgba32Compatible(byte luminance) + { + var original = new La16(luminance, luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Vector4 La16Vector = original.ToScaledVector4(); + Vector4 rgbaVector = original.ToScaledVector4(); + + Assert.Equal(La16Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void FromScaledVector4_IsRgba32Compatible(byte luminance) + { + var original = new La16(luminance, luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Vector4 rgbaVector = original.ToScaledVector4(); + + La16 mirror = default; + mirror.FromScaledVector4(rgbaVector); + + Assert.Equal(original, mirror); + } + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs new file mode 100644 index 0000000000..40739c69a2 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/La32Tests.cs @@ -0,0 +1,168 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public class La32Tests + { + [Fact] + public void AreEqual() + { + var color1 = new La32(3000, 100); + var color2 = new La32(3000, 100); + + Assert.Equal(color1, color2); + } + + [Fact] + public void AreNotEqual() + { + var color1 = new La32(12345, 100); + var color2 = new La32(54321, 100); + + Assert.NotEqual(color1, color2); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(65535, 4294967295)] + [InlineData(32767, 2147450879)] + [InlineData(42, 2752554)] + public void La32_PackedValue_EqualsInput(ushort input, uint packed) + => Assert.Equal(packed, new La32(input, input).PackedValue); + + [Fact] + public void La32_FromScaledVector4() + { + // Arrange + La32 gray = default; + const ushort expected = 32767; + Vector4 scaled = new La32(expected, expected).ToScaledVector4(); + + // Act + gray.FromScaledVector4(scaled); + ushort actual = gray.L; + ushort actualA = gray.A; + + // Assert + Assert.Equal(expected, actual); + Assert.Equal(expected, actualA); + } + + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(32767)] + public void La32_ToScaledVector4(ushort input) + { + // Arrange + var gray = new La32(input, input); + + // Act + Vector4 actual = gray.ToScaledVector4(); + + // Assert + float vectorInput = input / 65535F; + Assert.Equal(vectorInput, actual.X); + Assert.Equal(vectorInput, actual.Y); + Assert.Equal(vectorInput, actual.Z); + Assert.Equal(vectorInput, actual.W); + } + + [Fact] + public void La32_FromVector4() + { + // Arrange + La32 gray = default; + const ushort expected = 32767; + var vector = new La32(expected, expected).ToVector4(); + + // Act + gray.FromVector4(vector); + ushort actual = gray.L; + ushort actualA = gray.A; + + // Assert + Assert.Equal(expected, actual); + Assert.Equal(expected, actualA); + } + + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(32767)] + public void La32_ToVector4(ushort input) + { + // Arrange + var gray = new La32(input, input); + + // Act + var actual = gray.ToVector4(); + + // Assert + float vectorInput = input / 65535F; + Assert.Equal(vectorInput, actual.X); + Assert.Equal(vectorInput, actual.Y); + Assert.Equal(vectorInput, actual.Z); + Assert.Equal(vectorInput, actual.W); + } + + [Fact] + public void La32_FromRgba32() + { + // Arrange + La32 gray = default; + const byte rgb = 128; + ushort scaledRgb = ImageMaths.UpscaleFrom8BitTo16Bit(rgb); + ushort expected = ImageMaths.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); + + // Act + gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); + ushort actual = gray.L; + + // Assert + Assert.Equal(expected, actual); + Assert.Equal(ushort.MaxValue, gray.A); + } + + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(8100)] + public void La32_ToRgba32(ushort input) + { + // Arrange + ushort expected = ImageMaths.DownScaleFrom16BitTo8Bit(input); + var gray = new La32(input, ushort.MaxValue); + + // Act + Rgba32 actual = default; + gray.ToRgba32(ref actual); + + // Assert + Assert.Equal(expected, actual.R); + Assert.Equal(expected, actual.G); + Assert.Equal(expected, actual.B); + Assert.Equal(byte.MaxValue, actual.A); + } + + [Fact] + public void La32_FromBgra5551() + { + // arrange + var gray = default(La32); + ushort expected = ushort.MaxValue; + + // act + gray.FromBgra5551(new Bgra5551(1.0f, 1.0f, 1.0f, 1.0f)); + + // assert + Assert.Equal(expected, gray.L); + Assert.Equal(expected, gray.A); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 7687a7777c..7f02493b47 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 expected = Vector4.One; // act - byte4.FromGray8(new Gray8(byte.MaxValue)); + byte4.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expected, byte4.ToScaledVector4()); @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 expected = Vector4.One; // act - byte4.FromGray16(new Gray16(ushort.MaxValue)); + byte4.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expected, byte4.ToScaledVector4()); diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index b872e58b68..834bae685c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 expected = Vector4.One; // act - byte4.FromGray8(new Gray8(byte.MaxValue)); + byte4.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expected, byte4.ToScaledVector4()); @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 expected = Vector4.One; // act - byte4.FromGray16(new Gray16(ushort.MaxValue)); + byte4.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expected, byte4.ToScaledVector4()); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 09a78a6aa1..693dd6bd81 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders @@ -36,9 +36,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders using (Image src = srcFile.CreateRgba32Image()) using (Image dest = provider.GetImage()) { - GraphicsOptions options = new GraphicsOptions - { - AlphaCompositionMode = mode + var options = new GraphicsOptions + { + Antialias = false, + AlphaCompositionMode = mode }; using (Image res = dest.Clone(x => x.DrawImage(src, options))) @@ -53,4 +54,4 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 859be6b205..6706e4077a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.NormalSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.NormalSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -38,8 +38,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void NormalBlendFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.NormalSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.NormalSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.NormalSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData MultiplyFunctionData = new TheoryData { @@ -68,8 +68,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void MultiplyFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.MultiplySrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.MultiplySrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -77,8 +77,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void MultiplyFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.MultiplySrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.MultiplySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.MultiplySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData AddFunctionData = new TheoryData { @@ -107,8 +107,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.AddSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.AddSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -116,8 +116,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void AddFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.AddSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.AddSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.AddSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData SubtractFunctionData = new TheoryData { @@ -146,8 +146,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void SubtractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.SubtractSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.SubtractSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -155,8 +155,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void SubtractFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.SubtractSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.SubtractSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.SubtractSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData ScreenFunctionData = new TheoryData { @@ -185,8 +185,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void ScreenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.ScreenSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.ScreenSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -194,8 +194,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void ScreenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.ScreenSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.ScreenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -205,7 +205,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.ScreenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData DarkenFunctionData = new TheoryData { @@ -224,8 +224,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void DarkenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.DarkenSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.DarkenSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -233,8 +233,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void DarkenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.DarkenSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.DarkenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.DarkenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData LightenFunctionData = new TheoryData { @@ -263,8 +263,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void LightenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.LightenSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.LightenSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -272,8 +272,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void LightenFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.LightenSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.LightenSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.LightenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData OverlayFunctionData = new TheoryData { @@ -302,8 +302,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void OverlayFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.OverlaySrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.OverlaySrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -311,8 +311,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void OverlayFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.OverlaySrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.OverlaySrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -322,7 +322,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.OverlaySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } public static TheoryData HardLightFunctionData = new TheoryData { @@ -341,8 +341,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void HardLightFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.HardLightSrcOver((TPixel)back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = PorterDuffFunctions.HardLightSrcOver(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -350,8 +350,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void HardLightFunctionBlender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultPixelBlenders.HardLightSrcOver().Blend(back, source, amount); - VectorAssert.Equal(expected, actual, 2); + TPixel actual = new DefaultPixelBlenders.HardLightSrcOver().Blend(back.AsPixel(), source.AsPixel(), amount); + VectorAssert.Equal(expected.AsPixel(), actual, 2); } [Theory] @@ -361,7 +361,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { var dest = new Span(new TPixel[1]); new DefaultPixelBlenders.HardLightSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); - VectorAssert.Equal(expected, dest[0], 2); + VectorAssert.Equal(expected.AsPixel(), dest[0], 2); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs index d03cfeee22..6a678abc7f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.ReferenceImplementations.cs @@ -69,32 +69,32 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats return; } - // Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the + // L8 and L16 are special implementations of IPixel in that they do not conform to the // standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm. // One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and // packs/unpacks the pixel without and conversion so we employ custom methods do do this. - if (typeof(TDestinationPixel) == typeof(Gray16)) + if (typeof(TDestinationPixel) == typeof(L16)) { - ref Gray16 gray16Ref = ref MemoryMarshal.GetReference( - MemoryMarshal.Cast(destinationPixels)); + ref L16 L16Ref = ref MemoryMarshal.GetReference( + MemoryMarshal.Cast(destinationPixels)); for (int i = 0; i < count; i++) { ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray16 dp = ref Unsafe.Add(ref gray16Ref, i); + ref L16 dp = ref Unsafe.Add(ref L16Ref, i); dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); } return; } - if (typeof(TDestinationPixel) == typeof(Gray8)) + if (typeof(TDestinationPixel) == typeof(L8)) { - ref Gray8 gray8Ref = ref MemoryMarshal.GetReference( - MemoryMarshal.Cast(destinationPixels)); + ref L8 L8Ref = ref MemoryMarshal.GetReference( + MemoryMarshal.Cast(destinationPixels)); for (int i = 0; i < count; i++) { ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); - ref Gray8 dp = ref Unsafe.Add(ref gray8Ref, i); + ref L8 dp = ref Unsafe.Add(ref L8Ref, i); dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.L16OperationsTests.cs similarity index 70% rename from tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs rename to tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.L16OperationsTests.cs index 24b20eecab..ddcdc30bfd 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.L16OperationsTests.cs @@ -10,15 +10,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { public partial class PixelOperationsTests { - public class Gray8OperationsTests : PixelOperationsTests + public class L16OperationsTests : PixelOperationsTests { - public Gray8OperationsTests(ITestOutputHelper output) + public L16OperationsTests(ITestOutputHelper output) : base(output) { } [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.L8OperationsTests.cs similarity index 69% rename from tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs rename to tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.L8OperationsTests.cs index ffb1a0b027..b2a1a2dc3a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.L8OperationsTests.cs @@ -10,16 +10,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { public partial class PixelOperationsTests { - public class Gray16OperationsTests : PixelOperationsTests + public class L8OperationsTests : PixelOperationsTests { - public Gray16OperationsTests(ITestOutputHelper output) + public L8OperationsTests(ITestOutputHelper output) : base(output) { } [Fact] - public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); - + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.La16OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.La16OperationsTests.cs new file mode 100644 index 0000000000..f03aa5587f --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.La16OperationsTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class La16OperationsTests : PixelOperationsTests + { + public La16OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.La32OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.La32OperationsTests.cs new file mode 100644 index 0000000000..2112a2fea3 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.La32OperationsTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class La32OperationsTests : PixelOperationsTests + { + public La32OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 682da89fcc..9b6814f9a0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -279,20 +279,20 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations } - public static readonly TheoryData Generic_To_Data = new TheoryData - { - default(Rgba32), - default(Bgra32), - default(Rgb24), - default(Gray8), - default(Gray16), - default(Rgb48), - default(Rgba64) - }; + public static readonly TheoryData Generic_To_Data = new TheoryData + { + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel(), + new TestPixel() + }; [Theory] [MemberData(nameof(Generic_To_Data))] - public void Generic_To(TDestPixel dummy) + public void Generic_To(TestPixel dummy) where TDestPixel : struct, IPixel { const int Count = 2134; @@ -569,6 +569,236 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromBgra5551Bytes(int count) + { + int size = Unsafe.SizeOf(); + byte[] source = CreateByteTestData(count * size); + var expected = new TPixel[count]; + + for (int i = 0; i < count; i++) + { + int offset = i * size; + + Bgra5551 bgra = MemoryMarshal.Cast(source.AsSpan().Slice(offset, size))[0]; + expected[i].FromBgra5551(bgra); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromBgra5551Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToBgra5551Bytes(int count) + { + int size = Unsafe.SizeOf(); + TPixel[] source = CreatePixelTestData(count); + var expected = new byte[count * size]; + Bgra5551 bgra = default; + + for (int i = 0; i < count; i++) + { + int offset = i * size; + bgra.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes bytes = Unsafe.As(ref bgra); + expected[offset] = bytes[0]; + expected[offset + 1] = bytes[1]; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToBgra5551Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromL8(int count) + { + byte[] sourceBytes = CreateByteTestData(count); + L8[] source = sourceBytes.Select(b => new L8(b)).ToArray(); + var expected = new TPixel[count]; + + + for (int i = 0; i < count; i++) + { + expected[i].FromL8(source[i]); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromL8(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToL8(int count) + { + TPixel[] source = CreatePixelTestData(count); + var expected = new L8[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromScaledVector4(source[i].ToScaledVector4()); + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToL8(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromL16(int count) + { + L16[] source = CreateVector4TestData(count).Select(v => + { + L16 g = default; + g.FromVector4(v); + return g; + }).ToArray(); + + var expected = new TPixel[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromL16(source[i]); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromL16(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToL16(int count) + { + TPixel[] source = CreatePixelTestData(count); + var expected = new L16[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromScaledVector4(source[i].ToScaledVector4()); + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToL16(this.Configuration, s, d.GetSpan()) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromLa16Bytes(int count) + { + int size = Unsafe.SizeOf(); + byte[] source = CreateByteTestData(count * size); + var expected = new TPixel[count]; + + for (int i = 0; i < count; i++) + { + int offset = i * size; + + La16 la = MemoryMarshal.Cast(source.AsSpan().Slice(offset, size))[0]; + expected[i].FromLa16(la); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromLa16Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToLa16Bytes(int count) + { + int size = Unsafe.SizeOf(); + TPixel[] source = CreatePixelTestData(count); + var expected = new byte[count * size]; + La16 la = default; + + for (int i = 0; i < count; i++) + { + int offset = i * size; + la.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes bytes = Unsafe.As(ref la); + expected[offset] = bytes[0]; + expected[offset + 1] = bytes[1]; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToLa16Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromLa32Bytes(int count) + { + int size = Unsafe.SizeOf(); + byte[] source = CreateByteTestData(count * size); + var expected = new TPixel[count]; + + for (int i = 0; i < count; i++) + { + int offset = i * size; + + La32 la = MemoryMarshal.Cast(source.AsSpan().Slice(offset, size))[0]; + expected[i].FromLa32(la); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromLa32Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToLa32Bytes(int count) + { + var size = Unsafe.SizeOf(); + TPixel[] source = CreatePixelTestData(count); + var expected = new byte[count * size]; + La32 la = default; + + for (int i = 0; i < count; i++) + { + int offset = i * size; + la.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes bytes = Unsafe.As(ref la); + expected[offset] = bytes[0]; + expected[offset + 1] = bytes[1]; + expected[offset + 2] = bytes[2]; + expected[offset + 3] = bytes[3]; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToLa32Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + [Theory] [MemberData(nameof(ArraySizesData))] public void FromRgb24Bytes(int count) @@ -760,91 +990,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations ); } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void FromGray8(int count) - { - byte[] sourceBytes = CreateByteTestData(count); - Gray8[] source = sourceBytes.Select(b => new Gray8(b)).ToArray(); - var expected = new TPixel[count]; - - - for (int i = 0; i < count; i++) - { - expected[i].FromGray8(source[i]); - } - - TestOperation( - source, - expected, - (s, d) => Operations.FromGray8(this.Configuration, s, d.GetSpan()) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void ToGray8(int count) - { - TPixel[] source = CreatePixelTestData(count); - var expected = new Gray8[count]; - - for (int i = 0; i < count; i++) - { - expected[i].FromScaledVector4(source[i].ToScaledVector4()); - } - - TestOperation( - source, - expected, - (s, d) => Operations.ToGray8(this.Configuration, s, d.GetSpan()) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void FromGray16(int count) - { - Gray16[] source = CreateVector4TestData(count).Select(v => - { - Gray16 g = default; - g.FromVector4(v); - return g; - }).ToArray(); - - var expected = new TPixel[count]; - - for (int i = 0; i < count; i++) - { - expected[i].FromGray16(source[i]); - } - - TestOperation( - source, - expected, - (s, d) => Operations.FromGray16(this.Configuration, s, d.GetSpan()) - ); - } - - [Theory] - [MemberData(nameof(ArraySizesData))] - public void ToGray16(int count) - { - TPixel[] source = CreatePixelTestData(count); - var expected = new Gray16[count]; - - for (int i = 0; i < count; i++) - { - expected[i].FromScaledVector4(source[i].ToScaledVector4()); - } - - TestOperation( - source, - expected, - (s, d) => Operations.ToGray16(this.Configuration, s, d.GetSpan()) - ); - } - public delegate void RefAction(ref T1 arg1); internal static Vector4[] CreateExpectedVector4Data(TPixel[] source, RefAction vectorModifier = null) @@ -1016,18 +1161,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations // ReSharper restore PossibleNullReferenceException } } - else if (typeof(TDest) == typeof(Gray16)) - { - // Minor difference is tolerated for 16 bit pixel values - Span expected = MemoryMarshal.Cast(this.ExpectedDestBuffer.AsSpan()); - Span actual = MemoryMarshal.Cast(this.ActualDestBuffer.GetSpan()); - - for (int i = 0; i < count; i++) - { - int difference = expected[i].PackedValue - actual[i].PackedValue; - Assert.True(Math.Abs(difference) < 2); - } - } else { Span expected = this.ExpectedDestBuffer.AsSpan(); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index 3a28c82b7b..56cb526a4d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expectedPackedValue = uint.MaxValue; // act - rgba.FromGray8(new Gray8(byte.MaxValue)); + rgba.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); @@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expectedPackedValue = uint.MaxValue; // act - rgba.FromGray16(new Gray16(ushort.MaxValue)); + rgba.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expectedPackedValue, rgba.PackedValue); diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index fe2f8394ff..3a2841bb3b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 expected = Vector4.One; // act - rgba.FromGray16(new Gray16(ushort.MaxValue)); + rgba.FromL16(new L16(ushort.MaxValue)); // assert Assert.Equal(expected, rgba.ToScaledVector4()); @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Vector4 expected = Vector4.One; // act - rgba.FromGray8(new Gray8(byte.MaxValue)); + rgba.FromL8(new L8(byte.MaxValue)); // assert Assert.Equal(expected, rgba.ToScaledVector4()); diff --git a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs index 2fbe260ecd..80fcb4370a 100644 --- a/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/ColorMatrixTests.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using Xunit; diff --git a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs index d684198fa7..3e37cb30b5 100644 --- a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Primitives diff --git a/tests/ImageSharp.Tests/Primitives/PointFTests.cs b/tests/ImageSharp.Tests/Primitives/PointFTests.cs new file mode 100644 index 0000000000..2bb4cc6dda --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/PointFTests.cs @@ -0,0 +1,213 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using System.Numerics; +using System.Runtime.CompilerServices; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class PointFTests + { + private static readonly ApproximateFloatComparer ApproximateFloatComparer = + new ApproximateFloatComparer(1e-6f); + + [Fact] + public void CanReinterpretCastFromVector2() + { + var vector = new Vector2(1, 2); + + PointF point = Unsafe.As(ref vector); + + Assert.Equal(vector.X, point.X); + Assert.Equal(vector.Y, point.Y); + } + + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(default, PointF.Empty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(0.0, 0.0)] + public void NonDefaultConstructorTest(float x, float y) + { + var p1 = new PointF(x, y); + + Assert.Equal(x, p1.X); + Assert.Equal(y, p1.Y); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(PointF.Empty.IsEmpty); + Assert.True(default(PointF).IsEmpty); + Assert.True(new PointF(0, 0).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + public void IsEmptyRandomTest(float x, float y) + { + Assert.False(new PointF(x, y).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void CoordinatesTest(float x, float y) + { + var p = new PointF(x, y); + Assert.Equal(x, p.X); + Assert.Equal(y, p.Y); + + p.X = 10; + Assert.Equal(10, p.X); + + p.Y = -10.123f; + Assert.Equal(-10.123, p.Y, 3); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, int.MaxValue, int.MinValue)] + [InlineData(float.MinValue, float.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(0, 0, 0, 0)] + public void ArithmeticTestWithSize(float x, float y, int x1, int y1) + { + var p = new PointF(x, y); + var s = new Size(x1, y1); + + var addExpected = new PointF(x + x1, y + y1); + var subExpected = new PointF(x - x1, y - y1); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, PointF.Add(p, s)); + Assert.Equal(subExpected, PointF.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTestWithSizeF(float x, float y) + { + var p = new PointF(x, y); + var s = new SizeF(y, x); + + var addExpected = new PointF(x + y, y + x); + var subExpected = new PointF(x - y, y - x); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, PointF.Add(p, s)); + Assert.Equal(subExpected, PointF.Subtract(p, s)); + } + + [Fact] + public void RotateTest() + { + var p = new PointF(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); + + var pout = PointF.Transform(p, matrix); + + Assert.Equal(-2.82842732F, pout.X, ApproximateFloatComparer); + Assert.Equal(21.2132034F, pout.Y, ApproximateFloatComparer); + } + + [Fact] + public void SkewTest() + { + var p = new PointF(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, PointF.Empty); + + var pout = PointF.Transform(p, matrix); + Assert.Equal(new PointF(30, 30), pout); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(float x, float y) + { + var pLeft = new PointF(x, y); + var pRight = new PointF(y, x); + + if (x == y) + { + Assert.True(pLeft == pRight); + Assert.False(pLeft != pRight); + Assert.True(pLeft.Equals(pRight)); + Assert.True(pLeft.Equals((object)pRight)); + Assert.Equal(pLeft.GetHashCode(), pRight.GetHashCode()); + return; + } + + Assert.True(pLeft != pRight); + Assert.False(pLeft == pRight); + Assert.False(pLeft.Equals(pRight)); + Assert.False(pLeft.Equals((object)pRight)); + } + + [Fact] + public void EqualityTest_NotPointF() + { + var point = new PointF(0, 0); + Assert.False(point.Equals(null)); + Assert.False(point.Equals(0)); + + // If PointF implements IEquatable (e.g. in .NET Core), then structs that are implicitly + // convertible to var can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToPointF = typeof(IEquatable).IsAssignableFrom(point.GetType()); + Assert.Equal(expectsImplicitCastToPointF, point.Equals(new Point(0, 0))); + + Assert.False(point.Equals((object)new Point(0, 0))); // No implicit cast + } + + [Fact] + public void GetHashCodeTest() + { + var point = new PointF(10, 10); + Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode()); + } + + [Fact] + public void ToStringTest() + { + var p = new PointF(5.1F, -5.123F); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void DeconstructTest(float x, float y) + { + PointF p = new PointF(x, y); + + (float deconstructedX, float deconstructedY) = p; + + Assert.Equal(x, deconstructedX); + Assert.Equal(y, deconstructedY); + } + } +} diff --git a/tests/ImageSharp.Tests/Primitives/PointTests.cs b/tests/ImageSharp.Tests/Primitives/PointTests.cs new file mode 100644 index 0000000000..8e86c72188 --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/PointTests.cs @@ -0,0 +1,258 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using System.Numerics; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class PointTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(default, Point.Empty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(new Size(x, y)); + + Assert.Equal(p1, p2); + } + + [Theory] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + [InlineData(0)] + public void SingleIntConstructorTest(int x) + { + var p1 = new Point(x); + var p2 = new Point(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); + + Assert.Equal(p1, p2); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(Point.Empty.IsEmpty); + Assert.True(default(Point).IsEmpty); + Assert.True(new Point(0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void IsEmptyRandomTest(int x, int y) + { + Assert.False(new Point(x, y).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void CoordinatesTest(int x, int y) + { + var p = new Point(x, y); + Assert.Equal(x, p.X); + Assert.Equal(y, p.Y); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void PointFConversionTest(int x, int y) + { + PointF p = new Point(x, y); + Assert.Equal(new PointF(x, y), p); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void SizeConversionTest(int x, int y) + { + var sz = (Size)new Point(x, y); + Assert.Equal(new Size(x, y), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(int x, int y) + { + Point addExpected, subExpected, p = new Point(x, y); + var s = new Size(y, x); + + unchecked + { + addExpected = new Point(x + y, y + x); + subExpected = new Point(x - y, y - x); + } + + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, Point.Add(p, s)); + Assert.Equal(subExpected, Point.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void PointFMathematicalTest(float x, float y) + { + var pf = new PointF(x, y); + Point pCeiling, pTruncate, pRound; + + unchecked + { + pCeiling = new Point((int)MathF.Ceiling(x), (int)MathF.Ceiling(y)); + pTruncate = new Point((int)x, (int)y); + pRound = new Point((int)MathF.Round(x), (int)MathF.Round(y)); + } + + Assert.Equal(pCeiling, Point.Ceiling(pf)); + Assert.Equal(pRound, Point.Round(pf)); + Assert.Equal(pTruncate, (Point)pf); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void OffsetTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(y, x); + + p1.Offset(p2); + + Assert.Equal(unchecked(p2.X + p2.Y), p1.X); + Assert.Equal(p1.X, p1.Y); + + p2.Offset(x, y); + Assert.Equal(p1, p2); + } + + [Fact] + public void RotateTest() + { + var p = new Point(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, Point.Empty); + + var pout = Point.Transform(p, matrix); + + Assert.Equal(new Point(-3, 21), pout); + } + + [Fact] + public void SkewTest() + { + var p = new Point(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, Point.Empty); + + var pout = Point.Transform(p, matrix); + Assert.Equal(new Point(30, 30), pout); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point((x / 2) - 1, (y / 2) - 1); + var p3 = new Point(x, y); + + Assert.True(p1 == p3); + Assert.True(p1 != p2); + Assert.True(p2 != p3); + + Assert.True(p1.Equals(p3)); + Assert.False(p1.Equals(p2)); + Assert.False(p2.Equals(p3)); + + Assert.True(p1.Equals((object)p3)); + Assert.False(p1.Equals((object)p2)); + Assert.False(p2.Equals((object)p3)); + + Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); + } + + [Fact] + public void EqualityTest_NotPoint() + { + var point = new Point(0, 0); + Assert.False(point.Equals(null)); + Assert.False(point.Equals(0)); + Assert.False(point.Equals(new PointF(0, 0))); + } + + [Fact] + public void GetHashCodeTest() + { + var point = new Point(10, 10); + Assert.Equal(point.GetHashCode(), new Point(10, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new Point(20, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new Point(10, 20).GetHashCode()); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(1, -2, 3, -4)] + public void ConversionTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + RectangleF rectF = rect; + Assert.Equal(x, rectF.X); + Assert.Equal(y, rectF.Y); + Assert.Equal(width, rectF.Width); + Assert.Equal(height, rectF.Height); + } + + [Fact] + public void ToStringTest() + { + var p = new Point(5, -5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Point [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void DeconstructTest(int x, int y) + { + Point p = new Point(x, y); + + (int deconstructedX, int deconstructedY) = p; + + Assert.Equal(x, deconstructedX); + Assert.Equal(y, deconstructedY); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs b/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs new file mode 100644 index 0000000000..f0ba757160 --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/RectangleFTests.cs @@ -0,0 +1,285 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Tests the struct. + /// + public class RectangleFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(default, RectangleF.Empty); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void NonDefaultConstructorTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var p = new PointF(x, y); + var s = new SizeF(width, height); + var rect2 = new RectangleF(p, s); + + Assert.Equal(rect1, rect2); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void FromLTRBTest(float left, float top, float right, float bottom) + { + var expected = new RectangleF(left, top, right - left, bottom - top); + var actual = RectangleF.FromLTRB(left, top, right, bottom); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void DimensionsTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + var p = new PointF(x, y); + var s = new SizeF(width, height); + + Assert.Equal(p, rect.Location); + Assert.Equal(s, rect.Size); + Assert.Equal(x, rect.X); + Assert.Equal(y, rect.Y); + Assert.Equal(width, rect.Width); + Assert.Equal(height, rect.Height); + Assert.Equal(x, rect.Left); + Assert.Equal(y, rect.Top); + Assert.Equal(x + width, rect.Right); + Assert.Equal(y + height, rect.Bottom); + } + + [Fact] + public void IsEmptyTest() + { + Assert.True(RectangleF.Empty.IsEmpty); + Assert.True(default(RectangleF).IsEmpty); + Assert.True(new RectangleF(1, -2, -10, 10).IsEmpty); + Assert.True(new RectangleF(1, -2, 10, -10).IsEmpty); + Assert.True(new RectangleF(1, -2, 0, 0).IsEmpty); + + Assert.False(new RectangleF(0, 0, 10, 10).IsEmpty); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(float.MaxValue, float.MinValue)] + public void LocationSetTest(float x, float y) + { + var point = new PointF(x, y); + var rect = new RectangleF(10, 10, 10, 10) { Location = point }; + Assert.Equal(point, rect.Location); + Assert.Equal(point.X, rect.X); + Assert.Equal(point.Y, rect.Y); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(float.MaxValue, float.MinValue)] + public void SizeSetTest(float x, float y) + { + var size = new SizeF(x, y); + var rect = new RectangleF(10, 10, 10, 10) { Size = size }; + Assert.Equal(size, rect.Size); + Assert.Equal(size.Width, rect.Width); + Assert.Equal(size.Height, rect.Height); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void EqualityTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var rect2 = new RectangleF(width, height, x, y); + + Assert.True(rect1 != rect2); + Assert.False(rect1 == rect2); + Assert.False(rect1.Equals(rect2)); + Assert.False(rect1.Equals((object)rect2)); + } + + [Fact] + public void EqualityTestNotRectangleF() + { + var rectangle = new RectangleF(0, 0, 0, 0); + Assert.False(rectangle.Equals(null)); + Assert.False(rectangle.Equals(0)); + + // If RectangleF implements IEquatable (e.g. in .NET Core), then classes that are implicitly + // convertible to RectangleF can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToRectangleF = typeof(IEquatable).IsAssignableFrom(rectangle.GetType()); + Assert.Equal(expectsImplicitCastToRectangleF, rectangle.Equals(new Rectangle(0, 0, 0, 0))); + + Assert.False(rectangle.Equals((object)new Rectangle(0, 0, 0, 0))); // No implicit cast + } + + [Fact] + public void GetHashCodeTest() + { + var rect1 = new RectangleF(10, 10, 10, 10); + var rect2 = new RectangleF(10, 10, 10, 10); + Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(20, 10, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 20, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 20, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void ContainsTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + float x1 = (x + width) / 2; + float y1 = (y + height) / 2; + var p = new PointF(x1, y1); + var r = new RectangleF(x1, y1, width / 2, height / 2); + + Assert.False(rect.Contains(x1, y1)); + Assert.False(rect.Contains(p)); + Assert.False(rect.Contains(r)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue / 2, float.MinValue / 2, float.MinValue / 2, float.MaxValue / 2)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void InflateTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + var inflatedRect = new RectangleF(x - width, y - height, width + (2 * width), height + (2 * height)); + + rect.Inflate(width, height); + Assert.Equal(inflatedRect, rect); + + var s = new SizeF(x, y); + inflatedRect = RectangleF.Inflate(rect, x, y); + + rect.Inflate(s); + Assert.Equal(inflatedRect, rect); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue / 2, float.MinValue / 2)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void IntersectTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var rect2 = new RectangleF(y, x, width, height); + var expectedRect = RectangleF.Intersect(rect1, rect2); + rect1.Intersect(rect2); + Assert.Equal(expectedRect, rect1); + Assert.False(rect1.IntersectsWith(expectedRect)); + } + + [Fact] + public void IntersectIntersectingRectsTest() + { + var rect1 = new RectangleF(0, 0, 5, 5); + var rect2 = new RectangleF(1, 1, 3, 3); + var expected = new RectangleF(1, 1, 3, 3); + + Assert.Equal(expected, RectangleF.Intersect(rect1, rect2)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void UnionTest(float x, float y, float width, float height) + { + var a = new RectangleF(x, y, width, height); + var b = new RectangleF(width, height, x, y); + + float x1 = Math.Min(a.X, b.X); + float x2 = Math.Max(a.X + a.Width, b.X + b.Width); + float y1 = Math.Min(a.Y, b.Y); + float y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + var expectedRectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1); + + Assert.Equal(expectedRectangle, RectangleF.Union(a, b)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void OffsetTest(float x, float y, float width, float height) + { + var r1 = new RectangleF(x, y, width, height); + var expectedRect = new RectangleF(x + width, y + height, width, height); + var p = new PointF(width, height); + + r1.Offset(p); + Assert.Equal(expectedRect, r1); + + expectedRect.Offset(p); + r1.Offset(width, height); + Assert.Equal(expectedRect, r1); + } + + [Fact] + public void ToStringTest() + { + var r = new RectangleF(5, 5.1F, 1.3F, 1); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); + } + + [Theory] + [InlineData(float.MinValue, float.MaxValue, float.MaxValue, float.MaxValue)] + [InlineData(float.MinValue, float.MaxValue, float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue, float.MaxValue, float.MaxValue)] + [InlineData(float.MinValue, float.MinValue, float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, float.MinValue, float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue)] + [InlineData(float.MaxValue, float.MaxValue, float.MaxValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MaxValue)] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MinValue)] + [InlineData(0, 0, 0, 0)] + public void DeconstructTest(float x, float y, float width, float height) + { + RectangleF r = new RectangleF(x, y, width, height); + + (float dx, float dy, float dw, float dh) = r; + + Assert.Equal(x, dx); + Assert.Equal(y, dy); + Assert.Equal(width, dw); + Assert.Equal(height, dh); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Primitives/RectangleTests.cs b/tests/ImageSharp.Tests/Primitives/RectangleTests.cs new file mode 100644 index 0000000000..acfbe9e61c --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/RectangleTests.cs @@ -0,0 +1,336 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Tests the struct. + /// + public class RectangleTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(default, Rectangle.Empty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void NonDefaultConstructorTest(int x, int y, int width, int height) + { + var rect1 = new Rectangle(x, y, width, height); + var rect2 = new Rectangle(new Point(x, y), new Size(width, height)); + + Assert.Equal(rect1, rect2); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void FromLTRBTest(int left, int top, int right, int bottom) + { + var rect1 = new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); + var rect2 = Rectangle.FromLTRB(left, top, right, bottom); + + Assert.Equal(rect1, rect2); + } + + [Fact] + public void EmptyTest() + { + Assert.True(Rectangle.Empty.IsEmpty); + Assert.True(default(Rectangle).IsEmpty); + Assert.True(new Rectangle(0, 0, 0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void NonEmptyTest(int x, int y, int width, int height) + { + Assert.False(new Rectangle(x, y, width, height).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + public void DimensionsTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + Assert.Equal(new Point(x, y), rect.Location); + Assert.Equal(new Size(width, height), rect.Size); + + Assert.Equal(x, rect.X); + Assert.Equal(y, rect.Y); + Assert.Equal(width, rect.Width); + Assert.Equal(height, rect.Height); + Assert.Equal(x, rect.Left); + Assert.Equal(y, rect.Top); + Assert.Equal(unchecked(x + width), rect.Right); + Assert.Equal(unchecked(y + height), rect.Bottom); + + var p = new Point(width, height); + var s = new Size(x, y); + rect.Location = p; + rect.Size = s; + + Assert.Equal(p, rect.Location); + Assert.Equal(s, rect.Size); + + Assert.Equal(width, rect.X); + Assert.Equal(height, rect.Y); + Assert.Equal(x, rect.Width); + Assert.Equal(y, rect.Height); + Assert.Equal(width, rect.Left); + Assert.Equal(height, rect.Top); + Assert.Equal(unchecked(x + width), rect.Right); + Assert.Equal(unchecked(y + height), rect.Bottom); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(int.MaxValue, int.MinValue)] + public void LocationSetTest(int x, int y) + { + var point = new Point(x, y); + var rect = new Rectangle(10, 10, 10, 10) { Location = point }; + Assert.Equal(point, rect.Location); + Assert.Equal(point.X, rect.X); + Assert.Equal(point.Y, rect.Y); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(int.MaxValue, int.MinValue)] + public void SizeSetTest(int x, int y) + { + var size = new Size(x, y); + var rect = new Rectangle(10, 10, 10, 10) { Size = size }; + Assert.Equal(size, rect.Size); + Assert.Equal(size.Width, rect.Width); + Assert.Equal(size.Height, rect.Height); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + public void EqualityTest(int x, int y, int width, int height) + { + var rect1 = new Rectangle(x, y, width, height); + var rect2 = new Rectangle(width / 2, height / 2, x, y); + + Assert.True(rect1 != rect2); + Assert.False(rect1 == rect2); + Assert.False(rect1.Equals(rect2)); + Assert.False(rect1.Equals((object)rect2)); + } + + [Fact] + public void EqualityTestNotRectangle() + { + var rectangle = new Rectangle(0, 0, 0, 0); + Assert.False(rectangle.Equals(null)); + Assert.False(rectangle.Equals(0)); + Assert.False(rectangle.Equals(new RectangleF(0, 0, 0, 0))); + } + + [Fact] + public void GetHashCodeTest() + { + var rect1 = new Rectangle(10, 10, 10, 10); + var rect2 = new Rectangle(10, 10, 10, 10); + Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(20, 10, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 20, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 20, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)] + [InlineData(0, 0, 0, 0)] + public void RectangleFConversionTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + Rectangle rCeiling, rTruncate, rRound; + + unchecked + { + rCeiling = new Rectangle( + (int)Math.Ceiling(x), + (int)Math.Ceiling(y), + (int)Math.Ceiling(width), + (int)Math.Ceiling(height)); + + rTruncate = new Rectangle((int)x, (int)y, (int)width, (int)height); + + rRound = new Rectangle( + (int)Math.Round(x), + (int)Math.Round(y), + (int)Math.Round(width), + (int)Math.Round(height)); + } + + Assert.Equal(rCeiling, Rectangle.Ceiling(rect)); + Assert.Equal(rTruncate, Rectangle.Truncate(rect)); + Assert.Equal(rRound, Rectangle.Round(rect)); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void ContainsTest(int x, int y, int width, int height) + { + var rect = new Rectangle(unchecked((2 * x) - width), unchecked((2 * y) - height), width, height); + var p = new Point(x, y); + var r = new Rectangle(x, y, width / 2, height / 2); + + Assert.False(rect.Contains(x, y)); + Assert.False(rect.Contains(p)); + Assert.False(rect.Contains(r)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void InflateTest(int x, int y, int width, int height) + { + Rectangle inflatedRect, rect = new Rectangle(x, y, width, height); + unchecked + { + inflatedRect = new Rectangle(x - width, y - height, width + (2 * width), height + (2 * height)); + } + + Assert.Equal(inflatedRect, Rectangle.Inflate(rect, width, height)); + + rect.Inflate(width, height); + Assert.Equal(inflatedRect, rect); + + var s = new Size(x, y); + unchecked + { + inflatedRect = new Rectangle(rect.X - x, rect.Y - y, rect.Width + (2 * x), rect.Height + (2 * y)); + } + + rect.Inflate(s); + Assert.Equal(inflatedRect, rect); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void IntersectTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + var expectedRect = Rectangle.Intersect(rect, rect); + rect.Intersect(rect); + Assert.Equal(expectedRect, rect); + Assert.False(rect.IntersectsWith(expectedRect)); + } + + [Fact] + public void IntersectIntersectingRectsTest() + { + var rect1 = new Rectangle(0, 0, 5, 5); + var rect2 = new Rectangle(1, 1, 3, 3); + var expected = new Rectangle(1, 1, 3, 3); + + Assert.Equal(expected, Rectangle.Intersect(rect1, rect2)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, 0, 0, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void UnionTest(int x, int y, int width, int height) + { + var a = new Rectangle(x, y, width, height); + var b = new Rectangle(width, height, x, y); + + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.X + a.Width, b.X + b.Width); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + var expectedRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); + + Assert.Equal(expectedRectangle, Rectangle.Union(a, b)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, 0, 0, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void OffsetTest(int x, int y, int width, int height) + { + var r1 = new Rectangle(x, y, width, height); + var expectedRect = new Rectangle(x + width, y + height, width, height); + var p = new Point(width, height); + + r1.Offset(p); + Assert.Equal(expectedRect, r1); + + expectedRect.Offset(p); + r1.Offset(width, height); + Assert.Equal(expectedRect, r1); + } + + [Fact] + public void ToStringTest() + { + var r = new Rectangle(5, -5, 0, 1); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Rectangle [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); + } + + [Theory] + [InlineData(int.MinValue, int.MaxValue, int.MaxValue, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue, int.MaxValue, int.MaxValue)] + [InlineData(int.MinValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MinValue, int.MinValue, int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)] + [InlineData(int.MaxValue, int.MaxValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MaxValue)] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MinValue)] + [InlineData(0, 0, 0, 0)] + public void DeconstructTest(int x, int y, int width, int height) + { + var r = new Rectangle(x, y, width, height); + + (int dx, int dy, int dw, int dh) = r; + + Assert.Equal(x, dx); + Assert.Equal(y, dy); + Assert.Equal(width, dw); + Assert.Equal(height, dh); + } + } +} diff --git a/tests/ImageSharp.Tests/Primitives/SizeFTests.cs b/tests/ImageSharp.Tests/Primitives/SizeFTests.cs new file mode 100644 index 0000000000..8cda5d4ebf --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/SizeFTests.cs @@ -0,0 +1,249 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class SizeFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(default, SizeF.Empty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorAndDimensionsTest(float width, float height) + { + var s1 = new SizeF(width, height); + var p1 = new PointF(width, height); + var s2 = new SizeF(s1); + + Assert.Equal(s1, s2); + Assert.Equal(s1, new SizeF(p1)); + Assert.Equal(s2, new SizeF(p1)); + + Assert.Equal(width, s1.Width); + Assert.Equal(height, s1.Height); + + s1.Width = 10; + Assert.Equal(10, s1.Width); + + s1.Height = -10.123f; + Assert.Equal(-10.123, s1.Height, 3); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(SizeF.Empty.IsEmpty); + Assert.True(default(SizeF).IsEmpty); + Assert.True(new SizeF(0, 0).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + public void IsEmptyRandomTest(float width, float height) + { + Assert.False(new SizeF(width, height).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(float width, float height) + { + var s1 = new SizeF(width, height); + var s2 = new SizeF(height, width); + var addExpected = new SizeF(width + height, width + height); + var subExpected = new SizeF(width - height, height - width); + + Assert.Equal(addExpected, s1 + s2); + Assert.Equal(addExpected, SizeF.Add(s1, s2)); + + Assert.Equal(subExpected, s1 - s2); + Assert.Equal(subExpected, SizeF.Subtract(s1, s2)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(float width, float height) + { + var sLeft = new SizeF(width, height); + var sRight = new SizeF(height, width); + + if (width == height) + { + Assert.True(sLeft == sRight); + Assert.False(sLeft != sRight); + Assert.True(sLeft.Equals(sRight)); + Assert.True(sLeft.Equals((object)sRight)); + Assert.Equal(sLeft.GetHashCode(), sRight.GetHashCode()); + return; + } + + Assert.True(sLeft != sRight); + Assert.False(sLeft == sRight); + Assert.False(sLeft.Equals(sRight)); + Assert.False(sLeft.Equals((object)sRight)); + } + + [Fact] + public void EqualityTest_NotSizeF() + { + var size = new SizeF(0, 0); + Assert.False(size.Equals(null)); + Assert.False(size.Equals(0)); + + // If SizeF implements IEquatable (e.g in .NET Core), then classes that are implicitly + // convertible to SizeF can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToSizeF = typeof(IEquatable).IsAssignableFrom(size.GetType()); + Assert.Equal(expectsImplicitCastToSizeF, size.Equals(new Size(0, 0))); + + Assert.False(size.Equals((object)new Size(0, 0))); // No implicit cast + } + + [Fact] + public void GetHashCodeTest() + { + var size = new SizeF(10, 10); + Assert.Equal(size.GetHashCode(), new SizeF(10, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new SizeF(20, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new SizeF(10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void ConversionTest(float width, float height) + { + var s1 = new SizeF(width, height); + var p1 = (PointF)s1; + var s2 = new Size(unchecked((int)width), unchecked((int)height)); + + Assert.Equal(new PointF(width, height), p1); + Assert.Equal(p1, (PointF)s1); + Assert.Equal(s2, (Size)s1); + } + + [Fact] + public void ToStringTest() + { + var sz = new SizeF(10, 5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "SizeF [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); + } + + [Theory] + [InlineData(1000.234f, 0.0f)] + [InlineData(1000.234f, 1.0f)] + [InlineData(1000.234f, 2400.933f)] + [InlineData(1000.234f, float.MaxValue)] + [InlineData(1000.234f, -1.0f)] + [InlineData(1000.234f, -2400.933f)] + [InlineData(1000.234f, float.MinValue)] + [InlineData(float.MaxValue, 0.0f)] + [InlineData(float.MaxValue, 1.0f)] + [InlineData(float.MaxValue, 2400.933f)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(float.MaxValue, -1.0f)] + [InlineData(float.MaxValue, -2400.933f)] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, 0.0f)] + [InlineData(float.MinValue, 1.0f)] + [InlineData(float.MinValue, 2400.933f)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, -1.0f)] + [InlineData(float.MinValue, -2400.933f)] + [InlineData(float.MinValue, float.MinValue)] + public void MultiplicationTest(float dimension, float multiplier) + { + SizeF sz1 = new SizeF(dimension, dimension); + SizeF mulExpected; + + mulExpected = new SizeF(dimension * multiplier, dimension * multiplier); + + Assert.Equal(mulExpected, sz1 * multiplier); + Assert.Equal(mulExpected, multiplier * sz1); + } + + [Theory] + [InlineData(1111.1111f, 2222.2222f, 3333.3333f)] + public void MultiplicationTestWidthHeightMultiplier(float width, float height, float multiplier) + { + SizeF sz1 = new SizeF(width, height); + SizeF mulExpected; + + mulExpected = new SizeF(width * multiplier, height * multiplier); + + Assert.Equal(mulExpected, sz1 * multiplier); + Assert.Equal(mulExpected, multiplier * sz1); + } + + [Theory] + [InlineData(0.0f, 1.0f)] + [InlineData(1.0f, 1.0f)] + [InlineData(-1.0f, 1.0f)] + [InlineData(1.0f, -1.0f)] + [InlineData(-1.0f, -1.0f)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, 1.0f)] + [InlineData(float.MinValue, 1.0f)] + [InlineData(float.MaxValue, -1.0f)] + [InlineData(float.MinValue, -1.0f)] + [InlineData(float.MinValue, 0.0f)] + [InlineData(1.0f, float.MinValue)] + [InlineData(1.0f, float.MaxValue)] + [InlineData(-1.0f, float.MinValue)] + [InlineData(-1.0f, float.MaxValue)] + public void DivideTestSizeFloat(float dimension, float divisor) + { + SizeF size = new SizeF(dimension, dimension); + SizeF expected = new SizeF(dimension / divisor, dimension / divisor); + Assert.Equal(expected, size / divisor); + } + + [Theory] + [InlineData(-111.111f, 222.222f, 333.333f)] + public void DivideTestSizeFloatWidthHeightDivisor(float width, float height, float divisor) + { + SizeF size = new SizeF(width, height); + SizeF expected = new SizeF(width / divisor, height / divisor); + Assert.Equal(expected, size / divisor); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void DeconstructTest(float width, float height) + { + SizeF s = new SizeF(width, height); + + (float deconstructedWidth, float deconstructedHeight) = s; + + Assert.Equal(width, deconstructedWidth); + Assert.Equal(height, deconstructedHeight); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Primitives/SizeTests.cs b/tests/ImageSharp.Tests/Primitives/SizeTests.cs new file mode 100644 index 0000000000..4aea060366 --- /dev/null +++ b/tests/ImageSharp.Tests/Primitives/SizeTests.cs @@ -0,0 +1,379 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Tests the struct. + /// + public class SizeTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(default, Size.Empty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorTest(int width, int height) + { + var s1 = new Size(width, height); + var s2 = new Size(new Point(width, height)); + + Assert.Equal(s1, s2); + + s1.Width = 10; + Assert.Equal(10, s1.Width); + + s1.Height = -10; + Assert.Equal(-10, s1.Height); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(Size.Empty.IsEmpty); + Assert.True(default(Size).IsEmpty); + Assert.True(new Size(0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void IsEmptyRandomTest(int width, int height) + { + Assert.False(new Size(width, height).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void DimensionsTest(int width, int height) + { + var p = new Size(width, height); + Assert.Equal(width, p.Width); + Assert.Equal(height, p.Height); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void PointFConversionTest(int width, int height) + { + SizeF sz = new Size(width, height); + Assert.Equal(new SizeF(width, height), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void SizeConversionTest(int width, int height) + { + var sz = (Point)new Size(width, height); + Assert.Equal(new Point(width, height), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(int width, int height) + { + var sz1 = new Size(width, height); + var sz2 = new Size(height, width); + Size addExpected, subExpected; + + unchecked + { + addExpected = new Size(width + height, height + width); + subExpected = new Size(width - height, height - width); + } + + Assert.Equal(addExpected, sz1 + sz2); + Assert.Equal(subExpected, sz1 - sz2); + Assert.Equal(addExpected, Size.Add(sz1, sz2)); + Assert.Equal(subExpected, Size.Subtract(sz1, sz2)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void PointFMathematicalTest(float width, float height) + { + var szF = new SizeF(width, height); + Size pCeiling, pTruncate, pRound; + + unchecked + { + pCeiling = new Size((int)MathF.Ceiling(width), (int)MathF.Ceiling(height)); + pTruncate = new Size((int)width, (int)height); + pRound = new Size((int)MathF.Round(width), (int)MathF.Round(height)); + } + + Assert.Equal(pCeiling, Size.Ceiling(szF)); + Assert.Equal(pRound, Size.Round(szF)); + Assert.Equal(pTruncate, (Size)szF); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(int width, int height) + { + var p1 = new Size(width, height); + var p2 = new Size(unchecked(width - 1), unchecked(height - 1)); + var p3 = new Size(width, height); + + Assert.True(p1 == p3); + Assert.True(p1 != p2); + Assert.True(p2 != p3); + + Assert.True(p1.Equals(p3)); + Assert.False(p1.Equals(p2)); + Assert.False(p2.Equals(p3)); + + Assert.True(p1.Equals((object)p3)); + Assert.False(p1.Equals((object)p2)); + Assert.False(p2.Equals((object)p3)); + + Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); + } + + [Fact] + public void EqualityTest_NotSize() + { + var size = new Size(0, 0); + Assert.False(size.Equals(null)); + Assert.False(size.Equals(0)); + Assert.False(size.Equals(new SizeF(0, 0))); + } + + [Fact] + public void GetHashCodeTest() + { + var size = new Size(10, 10); + Assert.Equal(size.GetHashCode(), new Size(10, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new Size(20, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new Size(10, 20).GetHashCode()); + } + + [Fact] + public void ToStringTest() + { + var sz = new Size(10, 5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Size [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); + } + + [Theory] + [InlineData(1000, 0)] + [InlineData(1000, 1)] + [InlineData(1000, 2400)] + [InlineData(1000, int.MaxValue)] + [InlineData(1000, -1)] + [InlineData(1000, -2400)] + [InlineData(1000, int.MinValue)] + [InlineData(int.MaxValue, 0)] + [InlineData(int.MaxValue, 1)] + [InlineData(int.MaxValue, 2400)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(int.MaxValue, -1)] + [InlineData(int.MaxValue, -2400)] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, 0)] + [InlineData(int.MinValue, 1)] + [InlineData(int.MinValue, 2400)] + [InlineData(int.MinValue, int.MaxValue)] + [InlineData(int.MinValue, -1)] + [InlineData(int.MinValue, -2400)] + [InlineData(int.MinValue, int.MinValue)] + public void MultiplicationTestSizeInt(int dimension, int multiplier) + { + Size sz1 = new Size(dimension, dimension); + Size mulExpected; + + unchecked + { + mulExpected = new Size(dimension * multiplier, dimension * multiplier); + } + + Assert.Equal(mulExpected, sz1 * multiplier); + Assert.Equal(mulExpected, multiplier * sz1); + } + + [Theory] + [InlineData(1000, 2000, 3000)] + public void MultiplicationTestSizeIntWidthHeightMultiplier(int width, int height, int multiplier) + { + Size sz1 = new Size(width, height); + Size mulExpected; + + unchecked + { + mulExpected = new Size(width * multiplier, height * multiplier); + } + + Assert.Equal(mulExpected, sz1 * multiplier); + Assert.Equal(mulExpected, multiplier * sz1); + } + + [Theory] + [InlineData(1000, 0.0f)] + [InlineData(1000, 1.0f)] + [InlineData(1000, 2400.933f)] + [InlineData(1000, float.MaxValue)] + [InlineData(1000, -1.0f)] + [InlineData(1000, -2400.933f)] + [InlineData(1000, float.MinValue)] + [InlineData(int.MaxValue, 0.0f)] + [InlineData(int.MaxValue, 1.0f)] + [InlineData(int.MaxValue, 2400.933f)] + [InlineData(int.MaxValue, float.MaxValue)] + [InlineData(int.MaxValue, -1.0f)] + [InlineData(int.MaxValue, -2400.933f)] + [InlineData(int.MaxValue, float.MinValue)] + [InlineData(int.MinValue, 0.0f)] + [InlineData(int.MinValue, 1.0f)] + [InlineData(int.MinValue, 2400.933f)] + [InlineData(int.MinValue, float.MaxValue)] + [InlineData(int.MinValue, -1.0f)] + [InlineData(int.MinValue, -2400.933f)] + [InlineData(int.MinValue, float.MinValue)] + public void MultiplicationTestSizeFloat(int dimension, float multiplier) + { + Size sz1 = new Size(dimension, dimension); + SizeF mulExpected; + + mulExpected = new SizeF(dimension * multiplier, dimension * multiplier); + + Assert.Equal(mulExpected, sz1 * multiplier); + Assert.Equal(mulExpected, multiplier * sz1); + } + + [Theory] + [InlineData(1000, 2000, 30.33f)] + public void MultiplicationTestSizeFloatWidthHeightMultiplier(int width, int height, float multiplier) + { + Size sz1 = new Size(width, height); + SizeF mulExpected; + + mulExpected = new SizeF(width * multiplier, height * multiplier); + + Assert.Equal(mulExpected, sz1 * multiplier); + Assert.Equal(mulExpected, multiplier * sz1); + } + + [Fact] + public void DivideByZeroChecks() + { + Size size = new Size(100, 100); + Assert.Throws(() => size / 0); + + SizeF expectedSizeF = new SizeF(float.PositiveInfinity, float.PositiveInfinity); + Assert.Equal(expectedSizeF, size / 0.0f); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(-1, 1)] + [InlineData(1, -1)] + [InlineData(-1, -1)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MaxValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, 1)] + [InlineData(int.MinValue, 1)] + [InlineData(int.MaxValue, -1)] + public void DivideTestSizeInt(int dimension, int divisor) + { + Size size = new Size(dimension, dimension); + Size expected; + + expected = new Size(dimension / divisor, dimension / divisor); + + Assert.Equal(expected, size / divisor); + } + + [Theory] + [InlineData(1111, 2222, 3333)] + public void DivideTestSizeIntWidthHeightDivisor(int width, int height, int divisor) + { + Size size = new Size(width, height); + Size expected; + + expected = new Size(width / divisor, height / divisor); + + Assert.Equal(expected, size / divisor); + } + + [Theory] + [InlineData(0, 1.0f)] + [InlineData(1, 1.0f)] + [InlineData(-1, 1.0f)] + [InlineData(1, -1.0f)] + [InlineData(-1, -1.0f)] + [InlineData(int.MaxValue, float.MaxValue)] + [InlineData(int.MaxValue, float.MinValue)] + [InlineData(int.MinValue, float.MaxValue)] + [InlineData(int.MinValue, float.MinValue)] + [InlineData(int.MaxValue, 1.0f)] + [InlineData(int.MinValue, 1.0f)] + [InlineData(int.MaxValue, -1.0f)] + [InlineData(int.MinValue, -1.0f)] + public void DivideTestSizeFloat(int dimension, float divisor) + { + SizeF size = new SizeF(dimension, dimension); + SizeF expected; + + expected = new SizeF(dimension / divisor, dimension / divisor); + Assert.Equal(expected, size / divisor); + } + + [Theory] + [InlineData(1111, 2222, -333.33f)] + public void DivideTestSizeFloatWidthHeightDivisor(int width, int height, float divisor) + { + SizeF size = new SizeF(width, height); + SizeF expected; + + expected = new SizeF(width / divisor, height / divisor); + Assert.Equal(expected, size / divisor); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void DeconstructTest(int width, int height) + { + Size s = new Size(width, height); + + (int deconstructedWidth, int deconstructedHeight) = s; + + Assert.Equal(width, deconstructedWidth); + Assert.Equal(height, deconstructedHeight); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs index 140af9563a..82c22245d2 100644 --- a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// 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 SixLabors.Primitives; using Xunit; @@ -21,10 +21,10 @@ namespace SixLabors.ImageSharp.Tests.Processing public BaseImageOperationsExtensionTest() { - this.options = new GraphicsOptions(false); + this.options = new GraphicsOptions { Antialias = false }; this.source = new Image(91 + 324, 123 + 56); this.rect = new Rectangle(91, 123, 324, 56); // make this random? - this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(this.source, false); + this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(this.source.GetConfiguration(), this.source, false); this.operations = this.internalOperations; } @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Processing { return Assert.IsType(operation.NonGenericProcessor); } - + return Assert.IsType(operation.GenericProcessor); } @@ -49,12 +49,12 @@ namespace SixLabors.ImageSharp.Tests.Processing FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; Assert.Equal(rect, operation.Rectangle); - + if (operation.NonGenericProcessor != null) { return Assert.IsType(operation.NonGenericProcessor); } - + return Assert.IsType(operation.GenericProcessor); } } diff --git a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs index c98f910464..680db5a15b 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Dithering; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs index 8b3524fe66..7dc0ff5678 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/Processors/LaplacianKernelFactoryTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Convolution; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index c5d18cbb2b..53a50468b4 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs index 1b5bd656dc..a137a9f438 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -1,22 +1,24 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { public class BackgroundColorTest : BaseImageOperationsExtensionTest { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + [Fact] public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(Color.BlanchedAlmond); - var processor = this.Verify(); + BackgroundColorProcessor processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } @@ -24,9 +26,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(Color.BlanchedAlmond, this.rect); - var processor = this.Verify(this.rect); + BackgroundColorProcessor processor = this.Verify(this.rect); - Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } @@ -34,9 +36,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(this.options, Color.BlanchedAlmond); - var processor = this.Verify(); + BackgroundColorProcessor processor = this.Verify(); - Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(this.options, processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } @@ -44,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(this.options, Color.BlanchedAlmond, this.rect); - var processor = this.Verify(this.rect); + BackgroundColorProcessor processor = this.Verify(this.rect); - Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(this.options, processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs index 38d53c7b6c..bd7c0245b4 100644 --- a/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs @@ -3,13 +3,9 @@ using System.Collections.Generic; using System.Linq; - -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing { @@ -28,15 +24,17 @@ namespace SixLabors.ImageSharp.Tests.Processing .Where(x => x.Source == source); } - public IEnumerable.AppliedOperation> AppliedOperations(Image source) where TPixel : struct, IPixel + public IEnumerable.AppliedOperation> AppliedOperations(Image source) + where TPixel : struct, IPixel { return this.Created(source) .SelectMany(x => x.Applied); } - public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) where TPixel : struct, IPixel + public IInternalImageProcessingContext CreateImageProcessingContext(Configuration configuration, Image source, bool mutate) + where TPixel : struct, IPixel { - var op = new FakeImageOperations(source, mutate); + var op = new FakeImageOperations(configuration, source, mutate); this.ImageOperators.Add(op); return op; } @@ -44,11 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Processing public class FakeImageOperations : IInternalImageProcessingContext where TPixel : struct, IPixel { - private bool mutate; - - public FakeImageOperations(Image source, bool mutate) + public FakeImageOperations(Configuration configuration, Image source, bool mutate) { - this.mutate = mutate; + this.Configuration = configuration; this.Source = mutate ? source : source?.Clone(); } @@ -56,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.Processing public List Applied { get; } = new List(); - public MemoryAllocator MemoryAllocator => this.Source.GetConfiguration().MemoryAllocator; + public Configuration Configuration { get; } public Image GetResultImage() { @@ -71,19 +67,19 @@ namespace SixLabors.ImageSharp.Tests.Processing public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.Applied.Add(new AppliedOperation - { - Rectangle = rectangle, - NonGenericProcessor = processor - }); + { + Rectangle = rectangle, + NonGenericProcessor = processor + }); return this; } public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { this.Applied.Add(new AppliedOperation - { - NonGenericProcessor = processor - }); + { + NonGenericProcessor = processor + }); return this; } diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs new file mode 100644 index 0000000000..29d1d63e62 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class LightnessTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Lightness_amount_LightnessProcessorDefaultsSet() + { + this.operations.Lightness(.5F); + LightnessProcessor processor = this.Verify(); + + Assert.Equal(.5F, processor.Amount); + } + + [Fact] + public void Lightness_amount_rect_LightnessProcessorDefaultsSet() + { + this.operations.Lightness(.5F, this.rect); + LightnessProcessor processor = this.Verify(this.rect); + + Assert.Equal(.5F, processor.Amount); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs index 1b5c16538a..86a0d14854 100644 --- a/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Processing [Fact] public void ApplyProcessors_ListOfProcessors_AppliesAllProcessorsToOperation() { - var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); + var operations = new FakeImageOperationsProvider.FakeImageOperations(Configuration.Default, null, false); operations.ApplyProcessors(this.processorDefinition); Assert.Contains(this.processorDefinition, operations.Applied.Select(x => x.NonGenericProcessor)); } diff --git a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index 9d16583cd8..1c6a1c7ddc 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -5,7 +5,6 @@ using Moq; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing @@ -176,7 +175,7 @@ namespace SixLabors.ImageSharp.Tests.Processing } this.processorDefinition - .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Setup(p => p.CreatePixelSpecificProcessor(Configuration.Default, It.IsAny>(), It.IsAny())) .Returns(this.regularProcessorImpl.Object); } @@ -189,11 +188,11 @@ namespace SixLabors.ImageSharp.Tests.Processing } this.cloningProcessorDefinition - .Setup(p => p.CreatePixelSpecificCloningProcessor(It.IsAny>(), It.IsAny())) + .Setup(p => p.CreatePixelSpecificCloningProcessor(Configuration.Default, It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); this.cloningProcessorDefinition - .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Setup(p => p.CreatePixelSpecificProcessor(Configuration.Default, It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); } } diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index c712325249..32da38621a 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -121,8 +121,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization /// See: https://github.com/SixLabors/ImageSharp/pull/984 /// [Theory] - [WithTestPatternImages(110, 110, PixelTypes.Rgba32)] - [WithTestPatternImages(170, 170, PixelTypes.Rgba32)] + [WithTestPatternImages(110, 110, PixelTypes.Rgb24)] + [WithTestPatternImages(170, 170, PixelTypes.Rgb24)] public void Issue984(TestImageProvider provider) where TPixel : struct, IPixel { @@ -133,10 +133,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 256, ClipHistogram = true, + ClipLimit = 5, NumberOfTiles = 10 }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); } } } diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 978fd416bc..f7d6eba974 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,24 +1,25 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Overlays { public class GlowTest : BaseImageOperationsExtensionTest { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + [Fact] public void Glow_GlowProcessorWithDefaultValues() { this.operations.Glow(); - var p = this.Verify(); + GlowProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } @@ -27,9 +28,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_Color_GlowProcessorWithDefaultValues() { this.operations.Glow(Rgba32.Aquamarine); - var p = this.Verify(); + GlowProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Aquamarine, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } @@ -38,9 +39,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_Radux_GlowProcessorWithDefaultValues() { this.operations.Glow(3.5f); - var p = this.Verify(); + GlowProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.Absolute(3.5f), p.Radius); } @@ -50,11 +51,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { var rect = new Rectangle(12, 123, 43, 65); this.operations.Glow(rect); - var p = this.Verify(rect); + GlowProcessor p = this.Verify(rect); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.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 2484cf0cb8..da11edf738 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -1,23 +1,24 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Overlays { public class VignetteTest : BaseImageOperationsExtensionTest { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + [Fact] public void Vignette_VignetteProcessorWithDefaultValues() { this.operations.Vignette(); - var p = this.Verify(); + VignetteProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); @@ -27,9 +28,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Vignette_Color_VignetteProcessorWithDefaultValues() { this.operations.Vignette(Color.Aquamarine); - var p = this.Verify(); + VignetteProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Aquamarine, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); @@ -39,9 +40,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Vignette_Radux_VignetteProcessorWithDefaultValues() { this.operations.Vignette(3.5f, 12123f); - var p = this.Verify(); + VignetteProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX); Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY); @@ -52,12 +53,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { var rect = new Rectangle(12, 123, 43, 65); this.operations.Vignette(rect); - var p = this.Verify(rect); + VignetteProcessor p = this.Verify(rect); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.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/BinaryDitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs index d3507ed4c4..13be1dd2d6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs @@ -5,7 +5,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs index 4ae5d60513..4fe5012377 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -3,8 +3,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs index 0a10d0755f..05595eece9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs @@ -4,7 +4,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 296b8d1d7f..34b0165139 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -6,13 +6,12 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; - +using Microsoft.DotNet.RemoteExecutor; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -20,6 +19,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { public class BokehBlurTest { + static BokehBlurTest() + { + TestEnvironment.PrepareRemoteExecutor(); + } + private static readonly string Components10x2 = @" [[ 0.00451261+0.0165137j 0.02161237-0.00299122j 0.00387479-0.02682816j -0.02752798-0.01788438j -0.03553877+0.0154543j -0.01428268+0.04224722j @@ -58,8 +62,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution // Make sure the kernel components are the same using (var image = new Image(1, 1)) { + Configuration configuration = image.GetConfiguration(); var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma); - using (var processor = (BokehBlurProcessor)definition.CreatePixelSpecificProcessor(image, image.Bounds())) + using (var processor = (BokehBlurProcessor)definition.CreatePixelSpecificProcessor(configuration, image, image.Bounds())) { Assert.Equal(components.Count, processor.Kernels.Count); foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) @@ -125,20 +130,43 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution public void BokehBlurFilterProcessor(TestImageProvider provider, BokehBlurInfo value) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest( - x => x.BokehBlur(value.Radius, value.Components, value.Gamma), - testOutputDetails: value.ToString(), - appendPixelTypeToFileName: false); + static void RunTest(string providerDump, string infoDump) + { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + BokehBlurInfo value = BasicSerializer.Deserialize(infoDump); + + provider.RunValidatingProcessorTest( + x => x.BokehBlur(value.Radius, value.Components, value.Gamma), + testOutputDetails: value.ToString(), + appendPixelTypeToFileName: false); + + } + + RemoteExecutor + .Invoke(RunTest, BasicSerializer.Serialize(provider), BasicSerializer.Serialize(value)) + .Dispose(); } [Theory] - [WithTestPatternImages(200, 200, PixelTypes.Bgr24 | PixelTypes.Bgra32 | PixelTypes.Gray8)] + // TODO: Re-enable L8 when we update the reference images. + // [WithTestPatternImages(200, 200, PixelTypes.Bgr24 | PixelTypes.Bgra32 | PixelTypes.L8)] + [WithTestPatternImages(200, 200, PixelTypes.Bgr24 | PixelTypes.Bgra32)] public void BokehBlurFilterProcessor_WorksWithAllPixelTypes(TestImageProvider provider) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest( - x => x.BokehBlur(8, 2, 3), - appendSourceFileOrDescription: false); + static void RunTest(string providerDump) + { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + provider.RunValidatingProcessorTest( + x => x.BokehBlur(8, 2, 3), + appendSourceFileOrDescription: false); + } + + RemoteExecutor + .Invoke(RunTest, BasicSerializer.Serialize(provider)) + .Dispose(); } @@ -147,15 +175,26 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value) where TPixel : struct, IPixel { - provider.RunValidatingProcessorTest( - x => + static void RunTest(string providerDump, string infoDump) + { + TestImageProvider provider = + BasicSerializer.Deserialize>(providerDump); + BokehBlurInfo value = BasicSerializer.Deserialize(infoDump); + + provider.RunValidatingProcessorTest( + x => { Size size = x.GetCurrentSize(); var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2); x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds); }, - testOutputDetails: value.ToString(), - appendPixelTypeToFileName: false); + testOutputDetails: value.ToString(), + appendPixelTypeToFileName: false); + } + + RemoteExecutor + .Invoke(RunTest, BasicSerializer.Serialize(provider), BasicSerializer.Serialize(value)) + .Dispose(); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index a7cf9360cf..66e9ba2df3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index 05524b20b0..0e8013a641 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -4,7 +4,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 24ed090d8a..d1a3baa5a3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 96d223ee9f..535520cb8b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 5417bc1f34..5d65a9e61d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -1,9 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Dithering; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -149,4 +148,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization appendPixelTypeToFileName: false); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs new file mode 100644 index 0000000000..c18f043852 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelShaderTest.cs @@ -0,0 +1,113 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + [GroupOutput("Effects")] + public class PixelShaderTest + { + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void FullImage(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest( + x => x.ApplyPixelShaderProcessor( + span => + { + for (int i = 0; i < span.Length; i++) + { + Vector4 v4 = span[i]; + float avg = (v4.X + v4.Y + v4.Z) / 3f; + span[i] = new Vector4(avg); + } + }), + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => x.ApplyPixelShaderProcessor( + span => + { + for (int i = 0; i < span.Length; i++) + { + Vector4 v4 = span[i]; + float avg = (v4.X + v4.Y + v4.Z) / 3f; + span[i] = new Vector4(avg); + } + }, rect)); + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void PositionAwareFullImage(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunValidatingProcessorTest( + c => c.ApplyPixelShaderProcessor( + (span, offset) => + { + int y = offset.Y; + int x = offset.X; + for (int i = 0; i < span.Length; i++) + { + float + sine = MathF.Sin(y), + cosine = MathF.Cos(x + i), + sum = sine + cosine, + abs = MathF.Abs(sum), + a = 0.5f + (abs / 2); // Max value for sin(y) + cos(x) is 2 + + Vector4 v4 = span[i]; + float avg = (v4.X + v4.Y + v4.Z) / 3f; + var gray = new Vector4(avg, avg, avg, a); + + span[i] = Vector4.Clamp(gray, Vector4.Zero, Vector4.One); + } + }), + appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void PositionAwareInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.RunRectangleConstrainedValidatingProcessorTest( + (c, rect) => c.ApplyPixelShaderProcessor( + (span, offset) => + { + int y = offset.Y; + int x = offset.X; + for (int i = 0; i < span.Length; i++) + { + float + sine = MathF.Sin(y), + cosine = MathF.Cos(x + i), + sum = sine + cosine, + abs = MathF.Abs(sum), + a = 0.5f + (abs / 2); + + Vector4 v4 = span[i]; + float avg = (v4.X + v4.Y + v4.Z) / 3f; + var gray = new Vector4(avg, avg, avg, a); + + span[i] = Vector4.Clamp(gray, Vector4.Zero, Vector4.One); + } + }, rect)); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs index 68daa80eac..eca3da58ae 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs @@ -7,7 +7,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters { - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; [GroupOutput("Filters")] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs new file mode 100644 index 0000000000..c330ed6d9a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.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.Effects +{ + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + [GroupOutput("Filters")] + public class LightnessTest + { + private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.007F); + + public static readonly TheoryData LightnessValues + = new TheoryData + { + .5F, + 1.5F + }; + + [Theory] + [WithTestPatternImages(nameof(LightnessValues), 48, 48, PixelTypes.Rgba32)] + public void ApplyLightnessFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(ctx => ctx.Lightness(value), value, this.imageComparer); + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs index 5462a8b2da..e6a960f9e2 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs index c1c6bbd7cf..0c09b68723 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs @@ -4,7 +4,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs index 9448feefe2..470f48f781 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs index 50217e892d..b49ac3ea9a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -6,7 +6,6 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs index b351ec235f..cdc96f042a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 43384aee7c..763db47f99 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -12,7 +12,6 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.Memory; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; @@ -58,9 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } - [Theory( - Skip = "Debug only, enable manually" - )] + [Theory(Skip = "Debug only, enable manually")] [WithTestPatternImages(4000, 4000, PixelTypes.Rgba32, 300, 1024)] [WithTestPatternImages(3032, 3032, PixelTypes.Rgba32, 400, 1024)] [WithTestPatternImages(3032, 3032, PixelTypes.Rgba32, 400, 128)] @@ -135,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms destSize.Height, image0.Height, Configuration.Default.MemoryAllocator); - int minimumWorkerAllocationInBytes = verticalKernelMap.MaxDiameter * 2 * destSize.Width * SizeOfVector4; + int minimumWorkerAllocationInBytes = verticalKernelMap.MaxDiameter * 2 * destSize.Width * SizeOfVector4; verticalKernelMap.Dispose(); using (Image image = image0.Clone(configuration)) @@ -419,9 +416,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size(image.Width + 200, image.Height + 200), Mode = ResizeMode.BoxPad - }; + { + Size = new Size(image.Width + 200, image.Height + 200), + Mode = ResizeMode.BoxPad + }; image.Mutate(x => x.Resize(options)); @@ -462,6 +460,26 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } + [Theory] + [WithFile(TestImages.Jpeg.Issues.IncorrectResize1006, DefaultPixelType)] + public void CanResizeLargeImageWithCropMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(480, 600), + Mode = ResizeMode.Crop + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + } + [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] public void ResizeWithMaxMode(TestImageProvider provider) @@ -486,12 +504,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size( + { + Size = new Size( (int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)), - Mode = ResizeMode.Min - }; + Mode = ResizeMode.Min + }; image.Mutate(x => x.Resize(options)); @@ -508,9 +526,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size(image.Width + 200, image.Height), Mode = ResizeMode.Pad - }; + { + Size = new Size(image.Width + 200, image.Height), + Mode = ResizeMode.Pad + }; image.Mutate(x => x.Resize(options)); @@ -527,9 +546,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { var options = new ResizeOptions - { - Size = new Size(image.Width / 2, image.Height), Mode = ResizeMode.Stretch - }; + { + Size = new Size(image.Width / 2, image.Height), + Mode = ResizeMode.Stretch + }; image.Mutate(x => x.Resize(options)); @@ -537,5 +557,25 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms image.CompareToReferenceOutput(ValidatorComparer, provider); } } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, DefaultPixelType)] + [WithFile(TestImages.Jpeg.Issues.ExifGetString750Transform, DefaultPixelType)] + [WithFile(TestImages.Jpeg.Issues.ExifResize1049, DefaultPixelType)] + public void CanResizeExifIssueImages(TestImageProvider provider) + where TPixel : struct, IPixel + { + // Test images are large so skip on 32bit for now. + if (!TestEnvironment.Is64BitProcess) + { + return; + } + + using (Image image = provider.GetImage()) + { + // Don't bother saving, we're testing the EXIF metadata updates. + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); + } + } } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs index 70159e18ac..42017f3afb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs @@ -3,7 +3,6 @@ using System.Numerics; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs index ed6d3ef2bc..1c63d923aa 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs @@ -6,7 +6,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index 5350bd4a33..edf6a64403 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -5,7 +5,6 @@ using System; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs index d82cd1689d..309a73fb4b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs @@ -3,7 +3,6 @@ using System.Numerics; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index f268eda86c..f87e17e060 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index 71e3b71797..c13d4affdc 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Processing.Processors.Transforms; -using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index 3ac9af960b..9f8034fa37 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -15,12 +15,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { int xy = 1; - using (var img = new Image(xy, xy)) + 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); + profile.SetValue(ExifTag.PixelXDimension, xy + ushort.MaxValue); + profile.SetValue(ExifTag.PixelYDimension, xy + ushort.MaxValue); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); @@ -32,4 +32,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/RunExtendedTests.cmd b/tests/ImageSharp.Tests/RunExtendedTests.cmd deleted file mode 100644 index c2f4b9f537..0000000000 --- a/tests/ImageSharp.Tests/RunExtendedTests.cmd +++ /dev/null @@ -1,9 +0,0 @@ -dotnet build -c Release -dotnet xunit -nobuild -c Release -f net462 -dotnet xunit -nobuild -c Release -f net462 -x86 -dotnet xunit -nobuild -c Release -f net47 -dotnet xunit -nobuild -c Release -f net47 -x86 -dotnet xunit -nobuild -c Release -f net471 -dotnet xunit -nobuild -c Release -f net471 -x86 -dotnet xunit -nobuild -c Release -f net472 -dotnet xunit -nobuild -c Release -f net472 -x86 diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs index 799794ca4f..c8ddc8f758 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs @@ -5,7 +5,7 @@ using System.Numerics; namespace SixLabors.ImageSharp.Tests { - using SixLabors.ImageSharp.Primitives; + using SixLabors.ImageSharp; internal static class IccTestDataMatrix { diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 0a56110560..0f44b8e1cc 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -252,8 +252,10 @@ namespace SixLabors.ImageSharp.Tests public void FromBgra5551(Bgra5551 source) { } public void FromBgr24(Bgr24 source) { } public void FromBgra32(Bgra32 source) { } - public void FromGray8(Gray8 source) { } - public void FromGray16(Gray16 source) { } + public void FromL8(L8 source) { } + public void FromL16(L16 source) { } + public void FromLa16(La16 source) { } + public void FromLa32(La32 source) { } public void FromRgb24(Rgb24 source) { } public void FromRgba32(Rgba32 source) { } public void ToRgba32(ref Rgba32 dest) { } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 163d09bdde..f5cdb29b63 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -27,9 +27,9 @@ namespace SixLabors.ImageSharp.Tests 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 Gray16Bit = "Png/gray-16.png"; - public const string GrayAlpha8Bit = "Png/gray-alpha-8.png"; - public const string GrayAlpha8BitInterlaced = "Png/rollsroyce.png"; + public const string L16Bit = "Png/gray-16.png"; + public const string GrayA8Bit = "Png/gray-alpha-8.png"; + public const string GrayA8BitInterlaced = "Png/rollsroyce.png"; public const string GrayAlpha1BitInterlaced = "Png/iftbbn0g01.png"; public const string GrayAlpha2BitInterlaced = "Png/iftbbn0g02.png"; public const string Gray4BitInterlaced = "Png/iftbbn0g04.png"; @@ -52,9 +52,9 @@ namespace SixLabors.ImageSharp.Tests public const string Gray1BitTrans = "Png/gray-1-trns.png"; public const string Gray2BitTrans = "Png/gray-2-tRNS.png"; public const string Gray4BitTrans = "Png/gray-4-tRNS.png"; - public const string Gray8BitTrans = "Png/gray-8-tRNS.png"; + public const string L8BitTrans = "Png/gray-8-tRNS.png"; public const string LowColorVariance = "Png/low-variance.png"; - public const string PngWithMetaData = "Png/PngWithMetaData.png"; + public const string PngWithMetadata = "Png/PngWithMetaData.png"; public const string InvalidTextData = "Png/InvalidTextData.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html @@ -82,6 +82,14 @@ namespace SixLabors.ImageSharp.Tests public const string Ducky = "Png/ducky.png"; public const string Rainbow = "Png/rainbow.png"; + // Issue 1014: https://github.com/SixLabors/ImageSharp/issues/1014 + public const string Issue1014_1 = "Png/issues/Issue_1014_1.png"; + public const string Issue1014_2 = "Png/issues/Issue_1014_2.png"; + public const string Issue1014_3 = "Png/issues/Issue_1014_3.png"; + public const string Issue1014_4 = "Png/issues/Issue_1014_4.png"; + public const string Issue1014_5 = "Png/issues/Issue_1014_5.png"; + public const string Issue1014_6 = "Png/issues/Issue_1014_6.png"; + public static class Bad { // Odd chunk lengths @@ -91,6 +99,7 @@ namespace SixLabors.ImageSharp.Tests public const string ZlibOverflow = "Png/zlib-overflow.png"; public const string ZlibOverflow2 = "Png/zlib-overflow2.png"; public const string ZlibZtxtBadHeader = "Png/zlib-ztxt-bad-header.png"; + public const string Issue1047_BadEndChunk = "Png/issues/Issue_1047.png"; } public static readonly string[] All = @@ -181,6 +190,9 @@ namespace SixLabors.ImageSharp.Tests public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; public const string IncorrectQuality845 = "Jpg/issues/Issue845-Incorrect-Quality99.jpg"; public const string IncorrectColorspace855 = "Jpg/issues/issue855-incorrect-colorspace.jpg"; + public const string IncorrectResize1006 = "Jpg/issues/issue1006-incorrect-resize.jpg"; + public const string ExifResize1049 = "Jpg/issues/issue1049-exif-resize.jpg"; + public const string BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.jpg"; public static class Fuzz { @@ -357,5 +369,24 @@ namespace SixLabors.ImageSharp.Tests public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin, Leo, Ratio4x1, Ratio1x4 }; } + + public static class Tga + { + public const string Bit15 = "Tga/rgb15.tga"; + public const string Bit15Rle = "Tga/rgb15rle.tga"; + public const string Bit16 = "Tga/targa_16bit.tga"; + public const string Bit16PalRle = "Tga/ccm8.tga"; + public const string Bit24 = "Tga/targa_24bit.tga"; + public const string Bit24TopLeft = "Tga/targa_24bit_pal_origin_topleft.tga"; + public const string Bit24RleTopLeft = "Tga/targa_24bit_rle_origin_topleft.tga"; + public const string Bit32 = "Tga/targa_32bit.tga"; + public const string Grey = "Tga/targa_8bit.tga"; + public const string GreyRle = "Tga/targa_8bit_rle.tga"; + public const string Bit16Rle = "Tga/targa_16bit_rle.tga"; + public const string Bit24Rle = "Tga/targa_24bit_rle.tga"; + public const string Bit32Rle = "Tga/targa_32bit_rle.tga"; + public const string Bit16Pal = "Tga/targa_16bit_pal.tga"; + public const string Bit24Pal = "Tga/targa_24bit_pal.tga"; + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 872a935ffe..2ed4d9382c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Numerics; -using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Tests { diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index cdf5fcb089..5aed3b3647 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -31,21 +31,9 @@ namespace SixLabors.ImageSharp.Tests protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { - MethodInfo m = testMethod.DeclaringType.GetMethod(this.memberMethodName); - - Type[] args = factoryType.GetGenericArguments(); - Type colorType = args.Single(); - - Type imgType = typeof(Image<>).MakeGenericType(colorType); - - Type funcType = typeof(Func<>).MakeGenericType(imgType); - - MethodInfo genericMethod = m.MakeGenericMethod(args); - - Delegate d = genericMethod.CreateDelegate(funcType); - return new object[] { d }; + return new object[] { testMethod.DeclaringType.FullName, this.memberMethodName}; } protected override string GetFactoryMethodName(MethodInfo testMethod) => "Lambda"; } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs new file mode 100644 index 0000000000..48469db431 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/BasicSerializer.cs @@ -0,0 +1,97 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + /// + /// RemoteExecutor can only execute static methods, which can only consume string arguments, + /// because data is being passed on command line interface. This utility allows serialization + /// of types to strings. + /// + internal class BasicSerializer : IXunitSerializationInfo + { + private readonly Dictionary map = new Dictionary(); + + public const char Separator = ':'; + + private string DumpToString(Type type) + { + using var ms = new MemoryStream(); + using var writer = new StreamWriter(ms); + writer.WriteLine(type.FullName); + foreach (KeyValuePair kv in this.map) + { + writer.WriteLine($"{kv.Key}{Separator}{kv.Value}"); + } + writer.Flush(); + byte[] data = ms.ToArray(); + return System.Convert.ToBase64String(data); + } + + private Type LoadDump(string dump) + { + byte[] data = System.Convert.FromBase64String(dump); + + using var ms = new MemoryStream(data); + using var reader = new StreamReader(ms); + var type = Type.GetType(reader.ReadLine()); + for (string s = reader.ReadLine(); s != null ; s = reader.ReadLine()) + { + string[] kv = s.Split(Separator); + this.map[kv[0]] = kv[1]; + } + + return type; + } + + public static string Serialize(IXunitSerializable serializable) + { + var serializer = new BasicSerializer(); + serializable.Serialize(serializer); + return serializer.DumpToString(serializable.GetType()); + } + + public static T Deserialize(string dump) where T : IXunitSerializable + { + var serializer = new BasicSerializer(); + Type type = serializer.LoadDump(dump); + + var result = (T) Activator.CreateInstance(type); + result.Deserialize(serializer); + return result; + } + + public void AddValue(string key, object value, Type type = null) + { + Guard.NotNull(key, nameof(key)); + if (value == null) + { + return; + } + type ??= value.GetType(); + + this.map[key] = TypeDescriptor.GetConverter(type).ConvertToInvariantString(value); + } + + public object GetValue(string key, Type type) + { + Guard.NotNull(key, nameof(key)); + + if (!this.map.TryGetValue(key, out string str)) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + + return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(str); + } + + public T GetValue(string key) => (T)this.GetValue(key, typeof(T)); + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs new file mode 100644 index 0000000000..248755ea36 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + public class GraphicsOptionsComparer : IEqualityComparer + { + public bool Equals(GraphicsOptions x, GraphicsOptions y) + { + return x.AlphaCompositionMode == y.AlphaCompositionMode + && x.Antialias == y.Antialias + && x.AntialiasSubpixelDepth == y.AntialiasSubpixelDepth + && x.BlendPercentage == y.BlendPercentage + && x.ColorBlendingMode == y.ColorBlendingMode; + } + + public int GetHashCode(GraphicsOptions obj) => obj.GetHashCode(); + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs index 59167cc888..f5f709ce8d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison { public class ExactImageComparer : ImageComparer diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs index 024c2ee215..c44b89568b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs @@ -1,8 +1,6 @@ // 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 diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs index 38dada063c..95b3eb0245 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Linq; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison { public abstract class ImageComparer diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs index 1ffeb60ad4..30380218c3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs @@ -1,5 +1,4 @@ using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs index 8bed3a7155..cbcc6b8450 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -5,8 +5,6 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison { public class TolerantImageComparer : ImageComparer diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs deleted file mode 100644 index 5bd53a4c0c..0000000000 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Tests -{ - /// - /// Provides instances for parametric unit tests. - /// - /// The pixel format of the image - public abstract partial class TestImageProvider - where TPixel : struct, IPixel - { - private class LambdaProvider : TestImageProvider - { - private readonly Func> factoryFunc; - - public LambdaProvider(Func> factoryFunc) - { - this.factoryFunc = factoryFunc; - } - - public override Image GetImage() => this.factoryFunc(); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs new file mode 100644 index 0000000000..077dc622f8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/MemberMethodProvider.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Provides instances for parametric unit tests. + /// + /// The pixel format of the image + public abstract partial class TestImageProvider + where TPixel : struct, IPixel + { + private class MemberMethodProvider : TestImageProvider + { + private string declaringTypeName; + private string methodName; + private Func> factoryFunc; + + public MemberMethodProvider() + { + } + + public MemberMethodProvider(string declaringTypeName, string methodName) + { + this.declaringTypeName = declaringTypeName; + this.methodName = methodName; + } + + public override Image GetImage() + { + this.factoryFunc ??= this.GetFactory(); + return this.factoryFunc(); + } + + public override void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + + info.AddValue(nameof(this.declaringTypeName), this.declaringTypeName); + info.AddValue(nameof(this.methodName), this.methodName); + } + + public override void Deserialize(IXunitSerializationInfo info) + { + base.Deserialize(info); + + this.methodName = info.GetValue(nameof(this.methodName)); + this.declaringTypeName = info.GetValue(nameof(this.declaringTypeName)); + } + + private Func> GetFactory() + { + var declaringType = Type.GetType(this.declaringTypeName); + MethodInfo m = declaringType.GetMethod(this.methodName); + Type pixelType = typeof(TPixel); + Type imgType = typeof(Image<>).MakeGenericType(pixelType); + Type funcType = typeof(Func<>).MakeGenericType(imgType); + MethodInfo genericMethod = m.MakeGenericMethod(pixelType); + return (Func>) genericMethod.CreateDelegate(funcType); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 88b30ce34e..7da9707ef3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -1,9 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// 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.Abstractions; namespace SixLabors.ImageSharp.Tests @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests Image image = base.GetImage(); Color color = new Rgba32(this.r, this.g, this.b, this.a); - image.Mutate(x => x.Fill(color)); + image.GetPixelSpan().Fill(color.ToPixel()); return image; } @@ -78,4 +78,4 @@ namespace SixLabors.ImageSharp.Tests } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 63de4c96f4..8b165a8beb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests /// Provides instances for parametric unit tests. /// /// The pixel format of the image - public abstract partial class TestImageProvider : ITestImageProvider + public abstract partial class TestImageProvider : ITestImageProvider, IXunitSerializable where TPixel : struct, IPixel { public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType(); @@ -73,10 +73,11 @@ namespace SixLabors.ImageSharp.Tests } public static TestImageProvider Lambda( - Func> factoryFunc, + string declaringTypeName, + string methodName, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - => new LambdaProvider(factoryFunc).Init(testMethod, pixelTypeOverride); + => new MemberMethodProvider(declaringTypeName, methodName).Init(testMethod, pixelTypeOverride); public static TestImageProvider Solid( int width, diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index 36c08a8486..c795563134 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests { Undefined = 0, - Alpha8 = 1 << 0, + A8 = 1 << 0, Argb32 = 1 << 1, @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Tests Bgra5551 = 1 << 22, - Gray8 = 1 << 23, + L8 = 1 << 23, Gray16 = 1 << 24, diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index 79c19f2be7..1547edeeb2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int w = bmp.Width; int h = bmp.Height; - var fullRect = new Rectangle(0, 0, w, h); + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); if (bmp.PixelFormat != PixelFormat.Format32bppArgb) { @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int w = bmp.Width; int h = bmp.Height; - var fullRect = new Rectangle(0, 0, w, h); + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); if (bmp.PixelFormat != PixelFormat.Format24bppRgb) { @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int h = image.Height; var resultBitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb); - var fullRect = new Rectangle(0, 0, w, h); + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); BitmapData data = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat); try { @@ -171,4 +171,4 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return resultBitmap; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 7d06847223..e09b27c714 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests @@ -53,7 +54,8 @@ namespace SixLabors.ImageSharp.Tests { var cfg = new Configuration( new JpegConfigurationModule(), - new GifConfigurationModule() + new GifConfigurationModule(), + new TgaConfigurationModule() ); // Magick codecs should work on all platforms @@ -75,4 +77,4 @@ namespace SixLabors.ImageSharp.Tests return cfg; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index a5a3e332c7..1bc4f47c72 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -2,10 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Tests { @@ -45,13 +47,12 @@ namespace SixLabors.ImageSharp.Tests internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; + private static readonly FileInfo TestAssemblyFile = + new FileInfo(typeof(TestEnvironment).GetTypeInfo().Assembly.Location); + private static string GetSolutionDirectoryFullPathImpl() { - string assemblyLocation = typeof(TestEnvironment).GetTypeInfo().Assembly.Location; - - var assemblyFile = new FileInfo(assemblyLocation); - - DirectoryInfo directory = assemblyFile.Directory; + DirectoryInfo directory = TestAssemblyFile.Directory; while (!directory.EnumerateFiles(ImageSharpSolutionFileName).Any()) { @@ -62,20 +63,20 @@ namespace SixLabors.ImageSharp.Tests catch (Exception ex) { throw new Exception( - $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", + $"Unable to find ImageSharp solution directory from {TestAssemblyFile} because of {ex.GetType().Name}!", ex); } if (directory == null) { - throw new Exception($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); + throw new Exception($"Unable to find ImageSharp solution directory from {TestAssemblyFile}!"); } } return directory.FullName; } - private static string GetFullPath(string relativePath) => + private static string GetFullPath(string relativePath) => Path.Combine(SolutionDirectoryFullPath, relativePath) .Replace('\\', Path.DirectorySeparatorChar); @@ -83,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests /// 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.) /// @@ -100,13 +101,15 @@ namespace SixLabors.ImageSharp.Tests actualOutputFileName.Replace("ActualOutput", @"External\ReferenceOutput").Replace('\\', Path.DirectorySeparatorChar); internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - + internal static bool IsMono => Type.GetType("Mono.Runtime") != null; // https://stackoverflow.com/a/721194 internal static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); internal static bool Is64BitProcess => IntPtr.Size == 8; + internal static bool IsFramework => string.IsNullOrEmpty(NetCoreVersion); + /// /// Creates the image output directory. /// @@ -132,6 +135,95 @@ namespace SixLabors.ImageSharp.Tests return path; } + /// + /// Creates Microsoft.DotNet.RemoteExecutor.exe.config for .NET framework, + /// When running in 32 bits, enforces 32 bit execution of Microsoft.DotNet.RemoteExecutor.exe + /// with the help of CorFlags.exe found in Windows SDK. + /// + internal static void PrepareRemoteExecutor() + { + if (!IsFramework) + { + return; + } + + string remoteExecutorConfigPath = + Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe.config"); + + if (File.Exists(remoteExecutorConfigPath)) + { + // already prepared + return; + } + + string testProjectConfigPath = TestAssemblyFile.FullName + ".config"; + + File.Copy(testProjectConfigPath, remoteExecutorConfigPath); + + if (Is64BitProcess) + { + return; + } + + EnsureRemoteExecutorIs32Bit(); + } + + /// + /// Locate and run CorFlags.exe /32Bit+ + /// https://docs.microsoft.com/en-us/dotnet/framework/tools/corflags-exe-corflags-conversion-tool + /// + private static void EnsureRemoteExecutorIs32Bit() + { + string windowsSdksDir = Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES(x86)"), + "Microsoft SDKs", "Windows"); + + FileInfo corFlagsFile = Find(new DirectoryInfo(windowsSdksDir), "CorFlags.exe"); + + string remoteExecutorPath = Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe"); + + string args = $"{remoteExecutorPath} /32Bit+ /Force"; + + var si = new ProcessStartInfo() + { + FileName = corFlagsFile.FullName, + Arguments = args, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + using var proc = Process.Start(si); + proc.WaitForExit(); + string standardOutput = proc.StandardOutput.ReadToEnd(); + string standardError = proc.StandardError.ReadToEnd(); + + if (proc.ExitCode != 0) + { + throw new Exception( + $@"Failed to run {si.FileName} {si.Arguments}:\n STDOUT: {standardOutput}\n STDERR: {standardError}"); + } + + static FileInfo Find(DirectoryInfo root, string name) + { + FileInfo fi = root.EnumerateFiles(name).FirstOrDefault(); + if (fi != null) + { + return fi; + } + + foreach (DirectoryInfo dir in root.EnumerateDirectories()) + { + fi = Find(dir, name); + if (fi != null) + { + return fi; + } + } + + return null; + } + } + /// /// Solution borrowed from: /// https://github.com/dotnet/BenchmarkDotNet/issues/448#issuecomment-308424100 @@ -146,4 +238,4 @@ namespace SixLabors.ImageSharp.Tests return ""; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 95f7f81fda..dcbc4f0c3c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -7,14 +7,13 @@ using System.IO; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; @@ -678,16 +677,16 @@ namespace SixLabors.ImageSharp.Tests private class MakeOpaqueProcessor : IImageProcessor { - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - => new MakeOpaqueProcessor(source, sourceRectangle); + => new MakeOpaqueProcessor(configuration, source, sourceRectangle); } private class MakeOpaqueProcessor : ImageProcessor where TPixel : struct, IPixel { - public MakeOpaqueProcessor(Image source, Rectangle sourceRectangle) - : base(source, sourceRectangle) + public MakeOpaqueProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) { } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs index e1209a0c6a..fcda2eaa15 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; -using SixLabors.Memory; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Tests.Memory { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index 1e1a45f074..f3be50e9af 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -29,11 +29,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public float Blue { get; set; } public float Alpha { get; set; } - public static implicit operator TPixel(TestPixel d) - { - return d?.AsPixel() ?? default(TPixel); - } - public TPixel AsPixel() { TPixel pix = default(TPixel); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index b56ce05171..9ac8054ea0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -12,7 +12,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests { @@ -39,8 +38,8 @@ namespace SixLabors.ImageSharp.Tests ClrTypes2PixelTypes[defaultPixelFormatType] = PixelTypes.Rgba32; // Add PixelFormat types - string nameSpace = typeof(Alpha8).FullName; - nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(Alpha8).Name.Length - 1); + string nameSpace = typeof(A8).FullName; + nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(A8).Name.Length - 1); foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.Rgba32)) { string typeName = $"{nameSpace}.{pt}"; diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs new file mode 100644 index 0000000000..71ab5b2626 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/BasicSerializerTests.cs @@ -0,0 +1,62 @@ +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests +{ + public class BasicSerializerTests + { + class BaseObj : IXunitSerializable + { + public double Length { get; set; } + public string Name { get; set; } + public int Lives { get; set; } + + public virtual void Deserialize(IXunitSerializationInfo info) + { + info.AddValue(nameof(Length), Length); + info.AddValue(nameof(Name), Name); + info.AddValue(nameof(this.Lives), Lives); + } + + public virtual void Serialize(IXunitSerializationInfo info) + { + this.Length = info.GetValue(nameof(Length)); + this.Name = info.GetValue(nameof(Name)); + this.Lives = info.GetValue(nameof(Lives)); + } + } + + class DerivedObj : BaseObj + { + public double Strength { get; set; } + + public override void Deserialize(IXunitSerializationInfo info) + { + this.Strength = info.GetValue(nameof(Strength)); + base.Deserialize(info); + } + + public override void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + info.AddValue(nameof(Strength), Strength); + } + } + + [Fact] + public void SerializeDeserialize_ShouldPreserveValues() + { + var obj = new DerivedObj() {Length = 123.1, Name = "Lol123!", Lives = 7, Strength = 4.8}; + + string str = BasicSerializer.Serialize(obj); + BaseObj mirrorBase = BasicSerializer.Deserialize(str); + + DerivedObj mirror = Assert.IsType(mirrorBase); + Assert.Equal(obj.Length, mirror.Length); + Assert.Equal(obj.Name, mirror.Name); + Assert.Equal(obj.Lives, mirror.Lives); + Assert.Equal(obj.Strength, mirror.Strength); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs index 61db992988..97c87d4866 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs @@ -6,7 +6,6 @@ using Moq; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index b60439b488..3b4097820a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48Bpp)] [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppInterlaced)] [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppTrans)] - [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Gray16Bit)] + [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.L16Bit)] public void MagickDecode_16BitDepthImage_IsApproximatelyEquivalentTo_SystemDrawingResult(TestImageProvider dummyProvider, string testImage) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 096f78299b..30a47062bd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -1,10 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.IO; - -using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; @@ -33,29 +31,6 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Directory.Exists(path)); } - /// - /// We need this test to make sure that the netcoreapp2.1 test execution actually covers the netcoreapp2.1 build configuration of ImageSharp. - /// - [Fact] - public void ImageSharpAssemblyUnderTest_MatchesExpectedTargetFramework() - { - this.Output.WriteLine("NetCoreVersion: " + TestEnvironment.NetCoreVersion); - this.Output.WriteLine("ImageSharpBuiltAgainst: " + TestHelpers.ImageSharpBuiltAgainst); - - if (string.IsNullOrEmpty(TestEnvironment.NetCoreVersion)) - { - this.Output.WriteLine("Not running under .NET Core!"); - } - else if (TestEnvironment.NetCoreVersion.StartsWith("2.1")) - { - Assert.Equal("netcoreapp2.1", TestHelpers.ImageSharpBuiltAgainst); - } - else - { - Assert.Equal("netstandard2.0", TestHelpers.ImageSharpBuiltAgainst); - } - } - [Fact] public void SolutionDirectoryFullPath() { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 738465a6ed..416f4034f4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] - [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] + [WithBlankImages(1, 1, PixelTypes.A8, PixelTypes.A8)] [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) where TPixel : struct, IPixel => diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 301d0cebe6..64b217a028 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -99,13 +99,13 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ExpandAllTypes_1() { - PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.HalfVector2 | PixelTypes.Rgba32; + PixelTypes pixelTypes = PixelTypes.A8 | PixelTypes.Bgr565 | PixelTypes.HalfVector2 | PixelTypes.Rgba32; IEnumerable> expanded = pixelTypes.ExpandAllTypes(); Assert.Equal(4, expanded.Count()); - AssertContainsPixelType(PixelTypes.Alpha8, expanded); + AssertContainsPixelType(PixelTypes.A8, expanded); AssertContainsPixelType(PixelTypes.Bgr565, expanded); AssertContainsPixelType(PixelTypes.HalfVector2, expanded); AssertContainsPixelType(PixelTypes.Rgba32, expanded); diff --git a/tests/Images/External b/tests/Images/External index 99a2bc523c..fbba5e2a78 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 99a2bc523cd4eb00e37af20d1b2088fa11564c57 +Subproject commit fbba5e2a78aa479c0752dc0fd91ec25b4948704a diff --git a/tests/Images/Input/Jpg/issues/issue-1076-invalid-subsampling.jpg b/tests/Images/Input/Jpg/issues/issue-1076-invalid-subsampling.jpg new file mode 100644 index 0000000000..6cc3531a0b --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-1076-invalid-subsampling.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1507746a6c37697cb985fc7427709fd68478ff7cbdfe20f6cfbe7257ed6c7ccd +size 39149 diff --git a/tests/Images/Input/Jpg/issues/issue1006-incorrect-resize.jpg b/tests/Images/Input/Jpg/issues/issue1006-incorrect-resize.jpg new file mode 100644 index 0000000000..3880b869ed --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue1006-incorrect-resize.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90079722b2763f64ff7a47889a7775c9b63ed92239aeff4df437bd1b5a5ab540 +size 618142 diff --git a/tests/Images/Input/Jpg/issues/issue1049-exif-resize.jpg b/tests/Images/Input/Jpg/issues/issue1049-exif-resize.jpg new file mode 100644 index 0000000000..b3abb7d2f5 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue1049-exif-resize.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f313e48c5b9d4d1af2df44057970f03ddafc809862d14e8593f3e1fc0aef2c1 +size 718443 diff --git a/tests/Images/Input/Png/issues/Issue_1014_1.png b/tests/Images/Input/Png/issues/Issue_1014_1.png new file mode 100644 index 0000000000..2bdd826d63 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1014_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e986d0ff909fc92b0f325c6012ca4123674b239c54647bbdf3fc0c7ace3e4327 +size 3965 diff --git a/tests/Images/Input/Png/issues/Issue_1014_2.png b/tests/Images/Input/Png/issues/Issue_1014_2.png new file mode 100644 index 0000000000..224ee915ae --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1014_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e042b3aa6c17db70a9fe7fda4fae1c90388d32480ac44e9f87341279c845fd11 +size 6626 diff --git a/tests/Images/Input/Png/issues/Issue_1014_3.png b/tests/Images/Input/Png/issues/Issue_1014_3.png new file mode 100644 index 0000000000..b288f4380e --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1014_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ca6190682c99ec1c00fe2bac7fee86902bcf8a2db43a781452bf137eb1b962a +size 4157 diff --git a/tests/Images/Input/Png/issues/Issue_1014_4.png b/tests/Images/Input/Png/issues/Issue_1014_4.png new file mode 100644 index 0000000000..1fb3dd5397 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1014_4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03c6ee008225ac18f2966842772d5afa64e318f41b552c3d63d1a9fdd3544eff +size 3328 diff --git a/tests/Images/Input/Png/issues/Issue_1014_5.png b/tests/Images/Input/Png/issues/Issue_1014_5.png new file mode 100644 index 0000000000..98a06472cc --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1014_5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1163cd03933f2b23e1ae27baa848a5c2d3f41f0b3457a33dd9523c40e610076b +size 4085 diff --git a/tests/Images/Input/Png/issues/Issue_1014_6.png b/tests/Images/Input/Png/issues/Issue_1014_6.png new file mode 100644 index 0000000000..77871b29c7 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1014_6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c60f7be42794764f970149dd967bcd15c2f7f783b82a3edf528e7556eeb6c806 +size 2114 diff --git a/tests/Images/Input/Png/issues/Issue_1047.png b/tests/Images/Input/Png/issues/Issue_1047.png new file mode 100644 index 0000000000..7d5a53a9e5 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1047.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4768d4bc3a4aaddb8e3e5cbff2beb706abacfd5448d658564f001811dafd320a +size 44638 diff --git a/tests/Images/Input/Tga/ccm8.tga b/tests/Images/Input/Tga/ccm8.tga new file mode 100644 index 0000000000..ab92516355 --- /dev/null +++ b/tests/Images/Input/Tga/ccm8.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67b3ffaaa75561d8b959258d6b26a1f9ca3228b02a3df98a614ea43241aaea52 +size 9271 diff --git a/tests/Images/Input/Tga/rgb15.tga b/tests/Images/Input/Tga/rgb15.tga new file mode 100644 index 0000000000..870295b45a --- /dev/null +++ b/tests/Images/Input/Tga/rgb15.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:390cfff190bc41386fa134eca70ea0d3ffdc32a285c73278ed34046b09c46c9d +size 80537 diff --git a/tests/Images/Input/Tga/rgb15rle.tga b/tests/Images/Input/Tga/rgb15rle.tga new file mode 100644 index 0000000000..a45940fc98 --- /dev/null +++ b/tests/Images/Input/Tga/rgb15rle.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3219186fc9a9f859c99c2b31cf81e7f0ab4292956d22fc659e714d0cdb51cfa7 +size 19941 diff --git a/tests/Images/Input/Tga/targa.png b/tests/Images/Input/Tga/targa.png new file mode 100644 index 0000000000..c4933c0ebd --- /dev/null +++ b/tests/Images/Input/Tga/targa.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abc382cec34a04815bd53ad30707c6cdeeceece8731244244e2ab91026d60957 +size 106139 diff --git a/tests/Images/Input/Tga/targa_16bit.tga b/tests/Images/Input/Tga/targa_16bit.tga new file mode 100644 index 0000000000..6c4143c2ee --- /dev/null +++ b/tests/Images/Input/Tga/targa_16bit.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3adea897f8843b73d0042e23bdfbd0115a7f534df90699134e768df57061f46 +size 70518 diff --git a/tests/Images/Input/Tga/targa_16bit_pal.tga b/tests/Images/Input/Tga/targa_16bit_pal.tga new file mode 100644 index 0000000000..b25def7798 --- /dev/null +++ b/tests/Images/Input/Tga/targa_16bit_pal.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97a4ac0cecfe69e1b5c74db5288fb8ca3bf29968e3b5288c4e5ce03bb4f06915 +size 35780 diff --git a/tests/Images/Input/Tga/targa_16bit_rle.tga b/tests/Images/Input/Tga/targa_16bit_rle.tga new file mode 100644 index 0000000000..49ef0e998b --- /dev/null +++ b/tests/Images/Input/Tga/targa_16bit_rle.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47d7ebf37672ea846ce071155733697e34083de36aeaafaebd78317708feffde +size 19566 diff --git a/tests/Images/Input/Tga/targa_24bit.tga b/tests/Images/Input/Tga/targa_24bit.tga new file mode 100644 index 0000000000..82c22e2425 --- /dev/null +++ b/tests/Images/Input/Tga/targa_24bit.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35921b6250e43ba8e1fb125ebe4939a57a67efb0aa9eac0d3605bf90e93309b1 +size 105768 diff --git a/tests/Images/Input/Tga/targa_24bit_pal.tga b/tests/Images/Input/Tga/targa_24bit_pal.tga new file mode 100644 index 0000000000..abfbf588a6 --- /dev/null +++ b/tests/Images/Input/Tga/targa_24bit_pal.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4926969e5ae6c9af38d33fa18429de756c48d06edd87c5d27cb8d5232b066ab2 +size 36036 diff --git a/tests/Images/Input/Tga/targa_24bit_pal_origin_topleft.tga b/tests/Images/Input/Tga/targa_24bit_pal_origin_topleft.tga new file mode 100644 index 0000000000..b8c4071745 --- /dev/null +++ b/tests/Images/Input/Tga/targa_24bit_pal_origin_topleft.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1c52e538a7d134b20ff57e44b7e304d1b5effacac03a4481d169702796fb195 +size 36062 diff --git a/tests/Images/Input/Tga/targa_24bit_rle.tga b/tests/Images/Input/Tga/targa_24bit_rle.tga new file mode 100644 index 0000000000..d6af44c0a6 --- /dev/null +++ b/tests/Images/Input/Tga/targa_24bit_rle.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56a79ab92d84bbe8c7efbc2711051938fa3ba97b48830aea0cb1dafd7d1fe222 +size 37711 diff --git a/tests/Images/Input/Tga/targa_24bit_rle_origin_topleft.tga b/tests/Images/Input/Tga/targa_24bit_rle_origin_topleft.tga new file mode 100644 index 0000000000..9310c51a70 --- /dev/null +++ b/tests/Images/Input/Tga/targa_24bit_rle_origin_topleft.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30e8b6d01ebf9d227d2e9dcdd7b2641bf8f335107110dfff780351870217d4f4 +size 37102 diff --git a/tests/Images/Input/Tga/targa_32bit.tga b/tests/Images/Input/Tga/targa_32bit.tga new file mode 100644 index 0000000000..8b2a57c810 --- /dev/null +++ b/tests/Images/Input/Tga/targa_32bit.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3a220619e25e86bab01b01a2e231ee64fd004e047fa86016bf68de576877352 +size 141018 diff --git a/tests/Images/Input/Tga/targa_32bit_rle.tga b/tests/Images/Input/Tga/targa_32bit_rle.tga new file mode 100644 index 0000000000..b021a2cc15 --- /dev/null +++ b/tests/Images/Input/Tga/targa_32bit_rle.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f415d6a246909c18fe604248ab5fe27c74aff9a63df58d8cdeab7c4c3cbe056a +size 49994 diff --git a/tests/Images/Input/Tga/targa_8bit.tga b/tests/Images/Input/Tga/targa_8bit.tga new file mode 100644 index 0000000000..9b0512971e --- /dev/null +++ b/tests/Images/Input/Tga/targa_8bit.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6aaae46d0e55f32a72732fbe48ed9dc4044c53432999ab66e9475e45e40f0133 +size 35268 diff --git a/tests/Images/Input/Tga/targa_8bit_rle.tga b/tests/Images/Input/Tga/targa_8bit_rle.tga new file mode 100644 index 0000000000..d6a66def15 --- /dev/null +++ b/tests/Images/Input/Tga/targa_8bit_rle.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a18d7fd98bc9ab62276103b4e7b474be93b3d7241f4f06aa564e32150e205a71 +size 13145 diff --git a/tests/coverlet.runsettings b/tests/coverlet.runsettings new file mode 100644 index 0000000000..ee408a5f04 --- /dev/null +++ b/tests/coverlet.runsettings @@ -0,0 +1,14 @@ + + + + + + + lcov + [SixLabors.*]* + true + + + + +