Browse Source

Merge remote-tracking branch 'upstream/master' into PNGOptimisation

# Conflicts:
#	src/ImageSharp/Formats/Png/PngEncoderCore.cs
#	tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
pull/1574/head
popow 6 years ago
parent
commit
965360b49f
  1. 5
      .editorconfig
  2. 19
      .gitattributes
  3. 4
      .github/CONTRIBUTING.md
  4. 148
      .github/workflows/build-and-test.yml
  5. 4
      .gitignore
  6. 43
      .travis.yml
  7. 28
      .vscode/launch.json
  8. 31
      .vscode/tasks.json
  9. 3
      CODE_OF_CONDUCT.md
  10. 22
      CodeCoverage.runsettings
  11. 56
      Directory.Build.props
  12. 32
      Directory.Build.targets
  13. 7
      GitVersion.yml
  14. 97
      ImageSharp.sln
  15. 393
      ImageSharp.sln.DotSettings
  16. 26
      README.md
  17. 69
      appveyor.yml
  18. 17
      build.cmd
  19. 122
      build.ps1
  20. 3
      build/icons/imagesharp-logo-128.png
  21. 3
      build/icons/imagesharp-logo-256.png
  22. 3
      build/icons/imagesharp-logo-32.png
  23. 3
      build/icons/imagesharp-logo-512.png
  24. 3
      build/icons/imagesharp-logo-64.png
  25. 3
      build/icons/imagesharp-logo.png
  26. 1
      build/icons/imagesharp-logo.svg
  27. 13
      ci-build.ps1
  28. 11
      ci-pack.ps1
  29. 37
      ci-test.ps1
  30. 13
      codecov.yml
  31. 112
      run-tests.ps1
  32. 2
      shared-infrastructure
  33. 18
      src/Directory.Build.props
  34. 22
      src/Directory.Build.targets
  35. 32
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  36. 2
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings
  37. 36
      src/ImageSharp.Drawing/Primitives/Region.cs
  38. 24
      src/ImageSharp.Drawing/Primitives/ShapePath.cs
  39. 64
      src/ImageSharp.Drawing/Primitives/ShapeRegion.cs
  40. 96
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  41. 222
      src/ImageSharp.Drawing/Processing/Brushes.cs
  42. 35
      src/ImageSharp.Drawing/Processing/ColorStop.cs
  43. 22
      src/ImageSharp.Drawing/Processing/DrawingHelpers.cs
  44. 168
      src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs
  45. 106
      src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs
  46. 175
      src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs
  47. 106
      src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs
  48. 110
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs
  49. 103
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs
  50. 106
      src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs
  51. 103
      src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs
  52. 179
      src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs
  53. 76
      src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs
  54. 76
      src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs
  55. 64
      src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs
  56. 70
      src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs
  57. 66
      src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs
  58. 95
      src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs
  59. 179
      src/ImageSharp.Drawing/Processing/GradientBrush.cs
  60. 37
      src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs
  61. 38
      src/ImageSharp.Drawing/Processing/IBrush.cs
  62. 28
      src/ImageSharp.Drawing/Processing/IPen.cs
  63. 170
      src/ImageSharp.Drawing/Processing/ImageBrush.cs
  64. 161
      src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs
  65. 287
      src/ImageSharp.Drawing/Processing/PathGradientBrush.cs
  66. 184
      src/ImageSharp.Drawing/Processing/PatternBrush.cs
  67. 76
      src/ImageSharp.Drawing/Processing/Pen.cs
  68. 97
      src/ImageSharp.Drawing/Processing/Pens.cs
  69. 101
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  70. 115
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  71. 43
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  72. 121
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
  73. 51
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  74. 197
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
  75. 81
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  76. 453
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
  77. 104
      src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs
  78. 179
      src/ImageSharp.Drawing/Processing/RecolorBrush.cs
  79. 133
      src/ImageSharp.Drawing/Processing/SolidBrush.cs
  80. 163
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  81. 84
      src/ImageSharp.Drawing/Utils/QuickSort.cs
  82. 164
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  83. 56
      src/ImageSharp/Advanced/AotCompilerTools.cs
  84. 16
      src/ImageSharp/Advanced/IConfigurable.cs
  85. 16
      src/ImageSharp/Advanced/IConfigurationProvider.cs
  86. 2
      src/ImageSharp/Advanced/IImageVisitor.cs
  87. 17
      src/ImageSharp/Advanced/IPixelSource.cs
  88. 19
      src/ImageSharp/Advanced/IRowIntervalOperation.cs
  89. 23
      src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs
  90. 17
      src/ImageSharp/Advanced/IRowOperation.cs
  91. 22
      src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs
  92. 101
      src/ImageSharp/Advanced/ParallelExecutionSettings.cs
  93. 198
      src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs
  94. 288
      src/ImageSharp/Advanced/ParallelRowIterator.cs
  95. 211
      src/ImageSharp/Color/Color.NamedColors.cs
  96. 222
      src/ImageSharp/Color/Color.WernerPalette.cs
  97. 130
      src/ImageSharp/Color/Color.cs
  98. 79
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  99. 2
      src/ImageSharp/ColorSpaces/Cmyk.cs
  100. 4
      src/ImageSharp/ColorSpaces/Companding/LCompanding.cs

5
.editorconfig

@ -339,6 +339,7 @@ csharp_space_between_square_brackets = false
# warn when using var for built-in types,
# warn when using var when the type is not apparent, and
# warn when not using var when the type is apparent
# warn when using simplified "using" declaration
###############################################################################
[*.cs]
csharp_prefer_braces = true:silent
@ -367,6 +368,6 @@ csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = never
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = false:warning

19
.gitattributes

@ -62,14 +62,30 @@
# 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
*.otf binary
*.pbm binary
*.pdf binary
*.png binary
*.ttf binary
*.ppt binary
*.pptx binary
*.pvr binary
*.snk binary
*.tga binary
*.ttc binary
*.ttf binary
*.woff binary
*.woff2 binary
*.xls binary
*.xlsx binary
# diff as plain text
*.doc diff=astextplain
*.docx diff=astextplain
@ -77,6 +93,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

4
.github/CONTRIBUTING.md

@ -29,6 +29,10 @@
* Ask any question about how to use ImageSharp in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General).
#### Code of Conduct
This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org/) to clarify expected behavior in our community.
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
And please remember. ImageSharp is the work of a very, very, small number of developers who struggle balancing time to contribute to the project with family time and work commitments. We encourage you to pitch in and help make our vision of simple accessible imageprocessing available to all. Open Source can only exist with your help.
Thanks for reading!

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

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

43
.travis.yml

@ -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

28
.vscode/launch.json

@ -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}"
}
]
}

31
.vscode/tasks.json

@ -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"
}
]
}

3
CODE_OF_CONDUCT.md

@ -0,0 +1,3 @@
# Code of Conduct
This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org/) to clarify expected behavior in our community.
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).

22
CodeCoverage.runsettings

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CodeCoverage>
<ModulePaths>
<Include>
<ModulePath>.*ImageSharp.dll</ModulePath>
</Include>
<Exclude>
<ModulePath>.*tests*</ModulePath>
<ModulePath>.*Tests*</ModulePath>
</Exclude>
</ModulePaths>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>

56
Directory.Build.props

@ -14,7 +14,7 @@
<PropertyGroup>
<BaseArtifactsPath>$(MSBuildThisFileDirectory)artifacts/</BaseArtifactsPath>
<BaseArtifactsPathSuffix>$(ImageSharpProjectCategory)/$(MSBuildProjectName)</BaseArtifactsPathSuffix>
<RepositoryUrl>https://github.com/SixLabors/ImageSharp/</RepositoryUrl>
<RepositoryUrl Condition="'$(RepositoryUrl)' == ''">https://github.com/SixLabors/ImageSharp/</RepositoryUrl>
</PropertyGroup>
<!-- Default settings that explicitly differ from the Sdk.props defaults -->
@ -29,13 +29,38 @@
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!--
https://apisof.net/
+===================+=======+==========+=====================+=============+=================+====================+==============+=========+
| SUPPORTS | MATHF | HASHCODE | EXTENDED_INTRINSICS | SPAN_STREAM | ENCODING_STRING | RUNTIME_INTRINSICS | CODECOVERAGE | HOTPATH |
+===================+=======+==========+=====================+=============+=================+====================+==============+=========|
| netcoreapp3.1 | Y | Y | Y | Y | Y | Y | Y | Y |
| netcoreapp2.1 | Y | Y | Y | Y | Y | N | Y | N |
| netcoreapp2.0 | Y | N | N | N | N | N | Y | N |
| netstandard2.1 | Y | Y | N | Y | Y | N | Y | N |
| netstandard2.0 | N | N | N | N | N | N | Y | N |
| netstandard1.3 | N | N | N | N | N | N | N | N |
| net472 | N | N | Y | N | N | N | Y | N |
+===================+=======+==========+=====================+=============+=================+====================+==============+=========|
-->
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE;SUPPORTS_HOTPATH</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_CODECOVERAGE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<DefineConstants>$(DefineConstants);SUPPORTS_CODECOVERAGE</DefineConstants>
</PropertyGroup>
<!--TODO: Check what this is testing for and why does it fail?-->
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<PropertyGroup Condition="'$(TargetFramework)' == 'net472'">
<DefineConstants>$(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_CODECOVERAGE</DefineConstants>
</PropertyGroup>
<!-- Default settings that explicitly differ from the Sdk.targets defaults-->
@ -56,26 +81,33 @@
<Copyright>Copyright © Six Labors and Contributors</Copyright>
<Features>strict;IOperation</Features>
<HighEntropyVA>true</HighEntropyVA>
<LangVersion>7.3</LangVersion>
<LangVersion>8.0</LangVersion>
<NeutralLanguage>en</NeutralLanguage>
<OverwriteReadOnlyFiles>true</OverwriteReadOnlyFiles>
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png</PackageIconUrl>
<PackageIcon>sixlabors.imagesharp.128.png</PackageIcon>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<RepositoryType>git</RepositoryType>
<RestoreSources>
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;
<!-- Contains RemoteExecutor. Taken from: https://github.com/dotnet/runtime/blob/master/NuGet.config -->
https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json;
</RestoreSources>
<SixLaborsPublicKey>002400000c8000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97</SixLaborsPublicKey>
<SixLaborsPublicKey>00240000048000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97</SixLaborsPublicKey>
<UseSharedCompilation>true</UseSharedCompilation>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<!-- Package references which are consumed by all projects -->
<!-- Package references and additional files which are consumed by all projects -->
<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers.Toolset" IsImplicitlyDefined="true" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" IsImplicitlyDefined="true" />
<PackageReference Include="StyleCop.Analyzers" IsImplicitlyDefined="true" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)shared-infrastructure\stylecop.json" />
<!--NuGet package icon source-->
<None Include="$(MSBuildThisFileDirectory)shared-infrastructure\branding\icons\imagesharp\sixlabors.imagesharp.128.png" Pack="true" PackagePath="" />
</ItemGroup>
</Project>

32
Directory.Build.targets

@ -15,33 +15,23 @@
<DefineConstants>$(DefineConstants);$(OS)</DefineConstants>
</PropertyGroup>
<!-- Tool versions for tool references across all projects -->
<ItemGroup>
<DotNetCliToolReference Update="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<!-- Package versions for package references across all projects -->
<ItemGroup>
<PackageReference Update="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Update="Colourful" Version="2.0.2" />
<PackageReference Update="Magick.NET-Q16-AnyCPU" Version="7.12.0" />
<PackageReference Update="Microsoft.Net.Compilers.Toolset" Version="3.1.0" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Update="Moq" Version="4.10.0" />
<PackageReference Update="SixLabors.Core" Version="1.0.0-beta0008" />
<PackageReference Update="SixLabors.Fonts" Version="1.0.0-beta0009" />
<PackageReference Update="SixLabors.Shapes" Version="1.0.0-beta0009" />
<PackageReference Update="SixLabors.Shapes.Text" Version="1.0.0-beta0009" />
<PackageReference Update="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Update="System.Drawing.Common" Version="4.5.1" />
<!--Global Dependencies-->
<PackageReference Update="Microsoft.Net.Compilers.Toolset" PrivateAssets="All" Version="3.3.1" />
<PackageReference Update="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0" />
<PackageReference Update="StyleCop.Analyzers" PrivateAssets="All" Version="1.1.118" />
<!--Src Dependencies-->
<PackageReference Update="System.Buffers" Version="4.5.0" />
<PackageReference Update="System.IO.Compression" Version="4.3.0" />
<PackageReference Update="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
<PackageReference Update="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Update="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Update="System.Memory" Version="4.5.3" />
<PackageReference Update="System.Runtime.CompilerServices.Unsafe" Version="4.7" />
<PackageReference Update="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Update="System.ValueTuple" Version="4.5.0" />
<PackageReference Update="xunit" Version="2.3.1" />
<PackageReference Update="xunit.runner.console" Version="2.3.1" />
<PackageReference Update="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup>
</Project>

7
GitVersion.yml

@ -0,0 +1,7 @@
continuous-delivery-fallback-tag: ci
branches:
master:
tag: unstable
mode: ContinuousDeployment
pull-request:
tag: pr

97
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,20 @@ 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*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5
shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
@ -378,18 +358,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 +382,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 +417,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}

393
ImageSharp.sln.DotSettings

@ -1,393 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=StyleCop/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;Profile name="StyleCop"&gt;
&lt;CSUpdateFileHeader&gt;False&lt;/CSUpdateFileHeader&gt;
&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;
&lt;CSOptimizeUsings&gt;
&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;
&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;
&lt;RegionName&gt;&lt;/RegionName&gt;
&lt;/CSOptimizeUsings&gt;
&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;
&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;
&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">StyleCop</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/MODIFIERS_ORDER/@EntryValue">public protected internal private static new abstract virtual override sealed readonly extern unsafe volatile async</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">Field, Property, Event, Method</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_LINQ_QUERY/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_ARRAY_AND_OBJECT_INITIALIZER/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXPRESSION/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_FOR_STMT/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTIPLE_DECLARATION/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_CONSTRAINS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE_SHIFTED_2</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_AUTO_PROPERTY/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_FIELD/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_INVOCABLE/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_PROPERTY/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BETWEEN_USING_GROUPS/@EntryValue">1</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">NEXT_LINE_SHIFTED_2</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FIXED_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FOR_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_FOREACH_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_IFELSE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_WHILE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE_SHIFTED_2</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EXPR_MEMBER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CONSTRUCTOR_INITIALIZER_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">ALWAYS</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_TYPE_CONSTRAINTS_ON_SAME_LINE/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">ON_SINGLE_LINE</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_MULTIPLICATIVE_OP/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_BINARY_OPSIGN/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_FIRST_TYPE_PARAMETER_CONSTRAINT/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&#xD;
&lt;Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"&gt;&#xD;
&lt;TypePattern DisplayName="COM interfaces or structs"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Interface" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.ComImport" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;Kind Is="Struct" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="P/Invoke classes called 'NativeMethods' (StyleCop)"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;Name Is=".*NativeMethods" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="DataMember serialisation classes (StyleCop)"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.Serialization.DataMemberAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="Default Pattern (StyleCop)" RemoveRegions="All"&gt;&#xD;
&lt;Entry DisplayName="Constants"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Constant" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Static fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="200" DisplayName="Constructors and Destructors"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;Kind Is="Destructor" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Kind Order="Constructor Destructor" /&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Delegates"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Delegate" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Enums"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interfaces"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Interface" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public properties"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface properties"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other properties"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="1000" DisplayName="Public indexers"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="1000" DisplayName="Interface indexers"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="1000" DisplayName="Other indexers"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public methods and operators"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;Kind Is="Operator" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Operators"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Operator" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="600" DisplayName="Nested structs"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Struct" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry Priority="700" DisplayName="Nested classes"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;/Patterns&gt;</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">// Copyright (c) Six Labors and contributors.&#xD;
// Licensed under the Apache License, Version 2.0.&#xD;
</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AC/@EntryIndexedValue">AC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DC/@EntryIndexedValue">DC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DCT/@EntryIndexedValue">DCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EOF/@EntryIndexedValue">EOF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FDCT/@EntryIndexedValue">FDCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IDCT/@EntryIndexedValue">IDCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JPEG/@EntryIndexedValue">JPEG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MCU/@EntryIndexedValue">MCU</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PNG/@EntryIndexedValue">PNG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGB/@EntryIndexedValue">RGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RLE/@EntryIndexedValue">RLE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XY/@EntryIndexedValue">XY</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XYZ/@EntryIndexedValue">XYZ</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/EventHandlerPatternLong/@EntryValue">$object$_On$event$</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=EnumMember/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Interfaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Locals/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=MethodPropertyEvent/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Other/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Parameters/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PublicFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bgra/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Quantizer/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

26
README.md

@ -8,6 +8,8 @@ SixLabors.ImageSharp
<div align="center">
[![Build Status](https://img.shields.io/github/workflow/status/SixLabors/ImageSharp/Build/master)](https://github.com/SixLabors/ImageSharp/actions)
[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)
[![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 +37,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,23 +47,15 @@ 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
### Build Status
| |Build Status|Code Coverage|
|-------------|:----------:|:-----------:|
|**Linux/Mac**|[![Build Status](https://travis-ci.org/SixLabors/ImageSharp.svg)](https://travis-ci.org/SixLabors/ImageSharp)|[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)|
|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/m9pn907xdah3ca39/branch/master?svg=true)](https://ci.appveyor.com/project/six-labors/imagesharp/branch/master)|[![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp/branch/master/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp)|
### 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!
- Please read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/master/.github/CONTRIBUTING.md) before opening issues or pull requests!
### Code of Conduct
This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org/) to clarify expected behavior in our community.
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
### API
Our API is designed to be simple to consume. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix.
@ -76,7 +69,7 @@ using SixLabors.ImageSharp.PixelFormats;
// Image.Load(string path) is a shortcut for our default type.
// Other pixel formats use Image.Load<TPixel>(string path))
using (Image<Rgba32> image = Image.Load("foo.jpg"))
using (Image image = Image.Load("foo.jpg"))
{
image.Mutate(x => x
.Resize(image.Width / 2, image.Height / 2)
@ -109,9 +102,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**:
@ -151,6 +144,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

69
appveyor.yml

@ -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

17
build.cmd

@ -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

122
build.ps1

@ -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 }

3
build/icons/imagesharp-logo-128.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:148a268c589b628f5d0b5af0e86911a0b393c35b8b25233c71553657c88e0b96
size 7568

3
build/icons/imagesharp-logo-256.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7e4b2ff72aef1979500cd130c28490a00be116bb833bc96ca30c85dc0596099c
size 15413

3
build/icons/imagesharp-logo-32.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:021c12313afbdc65f58bfea8c7b436d5c2102513bb63d9e64ee2b61a1344c56a
size 1799

3
build/icons/imagesharp-logo-512.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ae54ae0035df1f8f1459081e2f1d5cceda6f88cca6ec015d8c0209bf0d34edf
size 32534

3
build/icons/imagesharp-logo-64.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:92896854265693f28f9a503b9093cb2c9a4a9b329f310732efdd9c6f6c3761bc
size 3736

3
build/icons/imagesharp-logo.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ae54ae0035df1f8f1459081e2f1d5cceda6f88cca6ec015d8c0209bf0d34edf
size 32534

1
build/icons/imagesharp-logo.svg

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M955.19-89.12c1.71.76 2.92 2.23 4.1 3.64-2.36.5-4.63-1.09-4.1-3.64z" fill="#f3f3f3"/><path d="M955.99 795.86c-.55-2.11 1.59-3.28 3.42-2.81-.92 1.17-1.92 2.39-3.42 2.81zm-824.02-1.84c2.01-.67 3.78.88 3.23 2.98-1.34-.69-2.38-1.78-3.23-2.98z" fill="#f4f4f4"/><g transform="translate(3 77.213) scale(.84333)"><path d="M360.394-81.917C332.176-34.178 310.797 7.886 286.16 51.626c-38.852 5.81-87.766 35.358-108.462 53.536-23.27-39.522-47.319-80.984-72.065-121.732 48.75-44.512 145.286-87.984 254.761-65.347z" fill="#fcee21"/><path d="M585.941 121.39a63184.8 63184.8 0 0 1-162.904-.018c-26.67-31.213-61.232-56.836-94.452-65.342 23.084-40.89 47.898-86.085 70.744-127.125 75.877 25.617 155.261 90.42 186.612 192.486z" fill="#ed7124"/><path d="M78.958 9.17c27.048 46.86 52.248 90.395 75.86 129.989-17.584 46.554-19.429 90.024-5.9 130.23-50.04.125-100.355.347-143.226.41-17.334-90.382 6.06-188.551 73.266-260.63z" fill="#35a849"/><path d="M331.618 50.646c39.7 5.695 90.586 40.866 105.695 70.748-44.912-.15-107.006-.198-146.153-.135 13.192-22.859 29.853-51.82 40.458-70.613z" fill="#cd5911"/><path d="M234.715 269.473c-28.712.242-63.776.092-89.229-.09-17.285-42.947-11.055-100.462 8.472-131.656 27.3 45.036 56.142 92.792 80.757 131.746z" fill="#298138"/><path d="M444.344 158.743c49.347.175 101.697.11 150.938.113 18.912 93.176-15.004 195.992-76.919 258.997-27.185-46.24-51.866-89.439-77.095-132.613l.15-.34c10.485-22.85 15.306-48.2 14.153-73.303-1.531-18.873-5.622-37.733-11.227-52.854z" fill="#cb202d"/><path d="M367.352 158.939c26.614.068 65.477-.264 88.655-.172 17.291 42.172 7.163 95.713-11.41 132.164-24.437-42.171-54.894-93.243-77.245-131.992z" fill="#9f1923"/><path d="M165.551 305.827c23.184 34.257 60.157 58.85 100.814 66.88-25.464 42.621-47.314 80.025-70.74 120.477C108.02 463.19 42.056 388.03 15.05 305.822a79196.89 79196.89 0 0 1 150.5.005z" fill="#085ba7"/><path d="M418.737 319.328l.11.18c24.153 41.18 48.356 82.328 72.668 123.418-71.641 59.524-168.65 81.977-254.913 62.306 25.735-44.76 51.465-88.751 77.103-133.568 41.19-8.196 81.388-26.52 105.046-52.336z" fill="#5f2c83"/><path d="M164.496 305.822c47.886.079 95.948-.024 140.829.009-12.442 22.552-39.753 68.221-39.3 67.463-40.85-7.405-79.046-31.74-101.53-67.472z" fill="#064076"/><path d="M380.797 255.307c12.77 21.268 27.863 46.872 40.484 68.227-26.416 30.286-70.536 50.076-110.301 52.908 21.612-38.994 48.471-86.745 69.817-121.135z" fill="#431f5d"/><ellipse ry="59.777" rx="59.861" cy="212.001" cx="300.001" fill="#e3018f"/><path d="M288.47 47.639c-22.376 38.536-52.246 89.024-71.803 122.212-13.891-22.894-28.313-46.46-40.324-66.915 15.85-17.392 51.175-48.759 112.127-55.297z" fill="#e7d803"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

13
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

11
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

37
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
}

13
codecov.yml

@ -1,4 +1,11 @@
ignore:
"src/ImageSharp/Common/Helpers/DebugGuard.cs"
# Documentation: https://docs.codecov.io/docs/codecov-yaml
codecov:
# Avoid "Missing base report"
# https://github.com/codecov/support/issues/363
# https://docs.codecov.io/docs/comparing-commits
allow_coverage_offsets: true
# Avoid Report Expired
# https://docs.codecov.io/docs/codecov-yaml#section-expired-reports
max_report_age: off

112
run-tests.ps1

@ -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

2
shared-infrastructure

@ -1 +1 @@
Subproject commit faf84e44ec90e8a42a7271bcd04fea76279efb08
Subproject commit ea561c249ba86352fe3b69e612b8072f3652eacb

18
src/Directory.Build.props

@ -22,20 +22,18 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)..\shared-infrastructure\stylecop.json" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<!-- DynamicProxyGenAssembly2 is needed so Moq can use our internals -->
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" PublicKey="0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKeyToken=null" />
<InternalsVisibleTo Include="ImageSharp.Benchmarks" />
<InternalsVisibleTo Include="SixLabors.ImageSharp.Sandbox46" />
<InternalsVisibleTo Include="SixLabors.ImageSharp.Tests" />
<InternalsVisibleTo Include="ImageSharp.Benchmarks" PublicKey="$(SixLaborsPublicKey)" />
<InternalsVisibleTo Include="ImageSharp.Tests.ProfilingSandbox" PublicKey="$(SixLaborsPublicKey)" />
<InternalsVisibleTo Include="SixLabors.ImageSharp.Tests" PublicKey="$(SixLaborsPublicKey)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
</ItemGroup>
</Project>

22
src/Directory.Build.targets

@ -50,6 +50,26 @@
<!-- Empty target so that `dotnet test` will work on the solution -->
<!-- https://github.com/Microsoft/vstest/issues/411 -->
<Target Name="VSTest" />
<Target Name="VSTest" Condition="'$(IsTestProject)' == 'true'"/>
<ItemGroup>
<!--Shared config files that have to exist at root level.-->
<ConfigFilesToCopy Include="..\..\shared-infrastructure\.editorconfig;..\..\shared-infrastructure\.gitattributes" />
</ItemGroup>
<!--Ensures our config files are up to date.-->
<Target Name="CopyFiles" BeforeTargets="Build">
<Copy SourceFiles="@(ConfigFilesToCopy)"
SkipUnchangedFiles = "true"
DestinationFolder="..\..\" />
</Target>
<!-- Allows regenerating T4-generated files at build time using MsBuild -->
<!-- Enable on Windows OS to build all T4 templates. TODO: XPlat
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
-->
</Project>

32
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>
<AssemblyTitle>SixLabors.ImageSharp.Drawing</AssemblyTitle>
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<PackageId>SixLabors.ImageSharp.Drawing</PackageId>
<PackageTags>Image Draw Shape Path Font</PackageTags>
<RootNamespace>SixLabors.ImageSharp</RootNamespace>
<TargetFrameworks>netcoreapp2.1;netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<!-- TODO: Include .NETSTANDARD2.1 when released-->
<PropertyGroup Condition=" $(TargetFramework.StartsWith('netcoreapp2')) ">
<DefineConstants>$(DefineConstants);SUPPORTS_MATHF</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" $(TargetFramework.StartsWith('netcoreapp2.1')) ">
<DefineConstants>$(DefineConstants);SUPPORTS_HASHCODE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.Fonts" />
<PackageReference Include="SixLabors.Shapes" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
</Project>

2
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings

@ -1,2 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cextensions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

36
src/ImageSharp.Drawing/Primitives/Region.cs

@ -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
{
/// <summary>
/// Represents a region of an image.
/// </summary>
public abstract class Region
{
/// <summary>
/// Gets the maximum number of intersections to could be returned.
/// </summary>
public abstract int MaxIntersections { get; }
/// <summary>
/// Gets the bounding box that entirely surrounds this region.
/// </summary>
/// <remarks>
/// This should always contains all possible points returned from <see cref="Scan"/>.
/// </remarks>
public abstract Rectangle Bounds { get; }
/// <summary>
/// Scans the X axis for intersections at the Y axis position.
/// </summary>
/// <param name="y">The position along the y axis to find intersections.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="configuration">A <see cref="Configuration"/> instance in the context of the caller.</param>
/// <returns>The number of intersections found.</returns>
public abstract int Scan(float y, Span<float> buffer, Configuration configuration);
}
}

24
src/ImageSharp.Drawing/Primitives/ShapePath.cs

@ -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
{
/// <summary>
/// A mapping between a <see cref="IPath"/> and a region.
/// </summary>
internal class ShapePath : ShapeRegion
{
/// <summary>
/// Initializes a new instance of the <see cref="ShapePath"/> class.
/// </summary>
/// <param name="shape">The shape.</param>
/// <param name="pen">The pen to apply to the shape.</param>
public ShapePath(IPath shape, IPen pen)
: base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern))
{
}
}
}

64
src/ImageSharp.Drawing/Primitives/ShapeRegion.cs

@ -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
{
/// <summary>
/// A mapping between a <see cref="IPath"/> and a region.
/// </summary>
internal class ShapeRegion : Region
{
/// <summary>
/// Initializes a new instance of the <see cref="ShapeRegion"/> class.
/// </summary>
/// <param name="shape">The shape.</param>
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);
}
/// <summary>
/// Gets the fillable shape
/// </summary>
public IPath Shape { get; }
/// <inheritdoc/>
public override int MaxIntersections => this.Shape.MaxIntersections;
/// <inheritdoc/>
public override Rectangle Bounds { get; }
/// <inheritdoc/>
public override int Scan(float y, Span<float> buffer, Configuration configuration)
{
var start = new PointF(this.Bounds.Left - 1, y);
var end = new PointF(this.Bounds.Right + 1, y);
using (IMemoryOwner<PointF> tempBuffer = configuration.MemoryAllocator.Allocate<PointF>(buffer.Length))
{
Span<PointF> 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;
}
}
}
}

96
src/ImageSharp.Drawing/Processing/BrushApplicator.cs

@ -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
{
/// <summary>
/// primitive that converts a point in to a color for discovering the fill color based on an implementation
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <seealso cref="System.IDisposable" />
public abstract class BrushApplicator<TPixel> : IDisposable // disposable will be required if/when there is an ImageBrush
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="BrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
internal BrushApplicator(ImageFrame<TPixel> target, GraphicsOptions options)
{
this.Target = target;
this.Options = options;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
}
/// <summary>
/// Gets the blender
/// </summary>
internal PixelBlender<TPixel> Blender { get; }
/// <summary>
/// Gets the destination
/// </summary>
protected ImageFrame<TPixel> Target { get; }
/// <summary>
/// Gets the blend percentage
/// </summary>
protected GraphicsOptions Options { get; }
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <returns>The a <typeparamref name="TPixel"/> that should be applied to the pixel.</returns>
internal abstract TPixel this[int x, int y] { get; }
/// <inheritdoc/>
public abstract void Dispose();
/// <summary>
/// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
/// </summary>
/// <param name="scanline">The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.</param>
/// <param name="x">The x position in the target pixel space that the start of the scanline data corresponds to.</param>
/// <param name="y">The y position in the target pixel space that whole scanline corresponds to.</param>
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(Span<float> scanline, int x, int y)
{
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> 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<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}
}

222
src/ImageSharp.Drawing/Processing/Brushes.cs

@ -1,222 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A collection of methods for creating generic brushes.
/// </summary>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static class Brushes
{
/// <summary>
/// Percent10 Hatch Pattern
/// </summary>
/// ---> 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 }
};
/// <summary>
/// Percent20 pattern.
/// </summary>
private static readonly bool[,] Percent20Pattern =
{
{ true, false, false, false },
{ false, false, true, false },
{ true, false, false, false },
{ false, false, true, false }
};
/// <summary>
/// Horizontal Hatch Pattern
/// </summary>
private static readonly bool[,] HorizontalPattern =
{
{ false },
{ true },
{ false },
{ false }
};
/// <summary>
/// Min Pattern
/// </summary>
private static readonly bool[,] MinPattern =
{
{ false },
{ false },
{ false },
{ true }
};
/// <summary>
/// Vertical Pattern
/// </summary>
private static readonly bool[,] VerticalPattern =
{
{ false, true, false, false },
};
/// <summary>
/// Forward Diagonal Pattern
/// </summary>
private static readonly bool[,] ForwardDiagonalPattern =
{
{ false, false, false, true },
{ false, false, true, false },
{ false, true, false, false },
{ true, false, false, false }
};
/// <summary>
/// Backward Diagonal Pattern
/// </summary>
private static readonly bool[,] BackwardDiagonalPattern =
{
{ true, false, false, false },
{ false, true, false, false },
{ false, false, true, false },
{ false, false, false, true }
};
/// <summary>
/// Create as brush that will paint a solid color
/// </summary>
/// <param name="color">The color.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static SolidBrush Solid(Color color) => new SolidBrush(color);
/// <summary>
/// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent10(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, Percent10Pattern);
/// <summary>
/// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent10(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, Percent10Pattern);
/// <summary>
/// Create as brush that will paint a Percent20 Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent20(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, Percent20Pattern);
/// <summary>
/// Create as brush that will paint a Percent20 Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent20(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, Percent20Pattern);
/// <summary>
/// Create as brush that will paint a Horizontal Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Horizontal(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, HorizontalPattern);
/// <summary>
/// Create as brush that will paint a Horizontal Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Horizontal(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, HorizontalPattern);
/// <summary>
/// Create as brush that will paint a Min Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Min(Color foreColor) => new PatternBrush(foreColor, Color.Transparent, MinPattern);
/// <summary>
/// Create as brush that will paint a Min Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Min(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, MinPattern);
/// <summary>
/// Create as brush that will paint a Vertical Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Vertical(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, VerticalPattern);
/// <summary>
/// Create as brush that will paint a Vertical Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Vertical(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, VerticalPattern);
/// <summary>
/// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush ForwardDiagonal(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, ForwardDiagonalPattern);
/// <summary>
/// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush ForwardDiagonal(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, ForwardDiagonalPattern);
/// <summary>
/// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush BackwardDiagonal(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, BackwardDiagonalPattern);
/// <summary>
/// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush BackwardDiagonal(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, BackwardDiagonalPattern);
}
}

35
src/ImageSharp.Drawing/Processing/ColorStop.cs

@ -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
{
/// <summary>
/// A struct that defines a single color stop.
/// </summary>
[DebuggerDisplay("ColorStop({Ratio} -> {Color}")]
public readonly struct ColorStop
{
/// <summary>
/// Initializes a new instance of the <see cref="ColorStop" /> struct.
/// </summary>
/// <param name="ratio">Where should it be? 0 is at the start, 1 at the end of the Gradient.</param>
/// <param name="color">What color should be used at that point?</param>
public ColorStop(float ratio, in Color color)
{
this.Ratio = ratio;
this.Color = color;
}
/// <summary>
/// Gets the point along the defined gradient axis.
/// </summary>
public float Ratio { get; }
/// <summary>
/// Gets the color to be used.
/// </summary>
public Color Color { get; }
}
}

22
src/ImageSharp.Drawing/Processing/DrawingHelpers.cs

@ -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
{
/// <summary>
/// Convert a <see cref="DenseMatrix{Color}"/> to a <see cref="DenseMatrix{T}"/> of the given pixel type.
/// </summary>
public static DenseMatrix<TPixel> ToPixelMatrix<TPixel>(this DenseMatrix<Color> colorMatrix, Configuration configuration)
where TPixel : struct, IPixel<TPixel>
{
var result = new DenseMatrix<TPixel>(colorMatrix.Columns, colorMatrix.Rows);
Color.ToPixel(configuration, colorMatrix.Span, result.Span);
return result;
}
}
}

168
src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs

@ -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
{
/// <summary>
/// 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.
/// </summary>
public sealed class EllipticGradientBrush : GradientBrush
{
private readonly PointF center;
private readonly PointF referenceAxisEnd;
private readonly float axisRatio;
/// <inheritdoc cref="GradientBrush" />
/// <param name="center">The center of the elliptical gradient and 0 for the color stops.</param>
/// <param name="referenceAxisEnd">The end point of the reference axis of the ellipse.</param>
/// <param name="axisRatio">
/// 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.
/// </param>
/// <param name="repetitionMode">Defines how the colors of the gradients are repeated.</param>
/// <param name="colorStops">the color stops as defined in base class.</param>
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;
}
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator<TPixel>(
source,
options,
this.center,
this.referenceAxisEnd,
this.axisRatio,
this.ColorStops,
this.RepetitionMode);
/// <inheritdoc />
private sealed class RadialGradientBrushApplicator<TPixel> : GradientBrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
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;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options</param>
/// <param name="center">Center of the ellipse</param>
/// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param>
/// <param name="axisRatio">
/// Ratio of the axis length's. Used to determine the length of the second axis,
/// the first is defined by <see cref="center"/> and <see cref="referenceAxisEnd"/>.</param>
/// <param name="colorStops">Definition of colors</param>
/// <param name="repetitionMode">Defines how the gradient colors are repeated.</param>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> 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);
}
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc />
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);
}
}
}
}

106
src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of Bezier paths to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawBezierExtensions
{
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
Color color,
float thickness,
params PointF[] points) =>
source.DrawBeziers(new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
params PointF[] points) =>
source.DrawBeziers(options, new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided points as an open Bezier path with the supplied pen
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
params PointF[] points) =>
source.Draw(options, pen, new Path(new CubicBezierLineSegment(points)));
/// <summary>
/// Draws the provided points as an open Bezier path with the supplied pen
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
IPen pen,
params PointF[] points) =>
source.Draw(pen, new Path(new CubicBezierLineSegment(points)));
}
}

175
src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs

@ -1,175 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
GraphicsOptions.Default.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
PixelColorBlendingMode colorBlending,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
colorBlending,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
GraphicsOptions options) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
options.ColorBlendingMode,
options.AlphaCompositionMode,
options.BlendPercentage));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
GraphicsOptions.Default.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
PixelColorBlendingMode colorBlending,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
colorBlending,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
GraphicsOptions options) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
options.ColorBlendingMode,
options.AlphaCompositionMode,
options.BlendPercentage));
}
}

106
src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of lines to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawLineExtensions
{
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
Color color,
float thickness,
params PointF[] points) =>
source.DrawLines(new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>>
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
params PointF[] points) =>
source.DrawLines(options, new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as an open Linear path with the supplied pen
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
params PointF[] points) =>
source.Draw(options, pen, new Path(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as an open Linear path with the supplied pen
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
IPen pen,
params PointF[] points) =>
source.Draw(pen, new Path(new LinearLineSegment(points)));
}
}

110
src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of collections of polygon outlines to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawPathCollectionExtensions
{
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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;
}
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext
Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) =>
source.Draw(GraphicsOptions.Default, pen, paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The shapes.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
IPathCollection paths) =>
source.Draw(options, new Pen(brush, thickness), paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
IBrush brush,
float thickness,
IPathCollection paths) =>
source.Draw(new Pen(brush, thickness), paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
IPathCollection paths) =>
source.Draw(options, new SolidBrush(color), thickness, paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
Color color,
float thickness,
IPathCollection paths) =>
source.Draw(new SolidBrush(color), thickness, paths);
}
}

103
src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of polygon outlines to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawPathExtensions
{
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
IPath path) =>
source.Fill(options, pen.StrokeFill, new ShapePath(path, pen));
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) =>
source.Draw(GraphicsOptions.Default, pen, path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
IPath path) =>
source.Draw(options, new Pen(brush, thickness), path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
IBrush brush,
float thickness,
IPath path) =>
source.Draw(new Pen(brush, thickness), path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
IPath path) =>
source.Draw(options, new SolidBrush(color), thickness, path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
Color color,
float thickness,
IPath path) =>
source.Draw(new SolidBrush(color), thickness, path);
}
}

106
src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of closed linear polygons to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawPolygonExtensions
{
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
Color color,
float thickness,
params PointF[] points) =>
source.DrawPolygon(new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
params PointF[] points) =>
source.DrawPolygon(options, new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided Pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
IPen pen,
params PointF[] points) =>
source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided Pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
params PointF[] points) =>
source.Draw(options, pen, new Polygon(new LinearLineSegment(points)));
}
}

103
src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of rectangles to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawRectangleExtensions
{
/// <summary>
/// Draws the outline of the rectangle with the provided pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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));
/// <summary>
/// Draws the outline of the rectangle with the provided pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) =>
source.Draw(GraphicsOptions.Default, pen, shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
RectangleF shape) =>
source.Draw(options, new Pen(brush, thickness), shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
IBrush brush,
float thickness,
RectangleF shape) =>
source.Draw(new Pen(brush, thickness), shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
RectangleF shape) =>
source.Draw(options, new SolidBrush(color), thickness, shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
Color color,
float thickness,
RectangleF shape) =>
source.Draw(new SolidBrush(color), thickness, shape);
}
}

179
src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the drawing of text to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawTextExtensions
{
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="color">The color.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
Color color,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, color, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="color">The color.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
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);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
IBrush brush,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, brush, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
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);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="pen">The pen.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
IPen pen,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, pen, location);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="pen">The pen.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
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);
/// <summary>
/// Draws the text onto the the image filled via the brush then outlined via the pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="pen">The pen.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
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);
/// <summary>
/// Draws the text using the default resolution of <value>72dpi</value> onto the the image filled via the brush then outlined via the pen.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="brush">The brush.</param>
/// <param name="pen">The pen.</param>
/// <param name="location">The location.</param>
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
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));
}
}

76
src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the filling of polygons with various brushes to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class FillPathBuilderExtensions
{
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
Action<PathBuilder> path)
{
var pb = new PathBuilder();
path(pb);
return source.Fill(options, brush, pb.Build());
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
IBrush brush,
Action<PathBuilder> path) =>
source.Fill(GraphicsOptions.Default, brush, path);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
Action<PathBuilder> path) =>
source.Fill(options, new SolidBrush(color), path);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
Color color,
Action<PathBuilder> path) =>
source.Fill(new SolidBrush(color), path);
}
}

76
src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the filling of collections of polygon outlines to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class FillPathCollectionExtensions
{
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="paths">The shapes.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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;
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
IBrush brush,
IPathCollection paths) =>
source.Fill(GraphicsOptions.Default, brush, paths);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
IPathCollection paths) =>
source.Fill(options, new SolidBrush(color), paths);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
Color color,
IPathCollection paths) =>
source.Fill(new SolidBrush(color), paths);
}
}

64
src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the filling of polygon outlines to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class FillPathExtensions
{
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
IPath path) =>
source.Fill(options, brush, new ShapeRegion(path));
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) =>
source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path));
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
IPath path) =>
source.Fill(options, new SolidBrush(color), path);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, IPath path) =>
source.Fill(new SolidBrush(color), path);
}
}

70
src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the filling of closed linear polygons to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class FillPolygonExtensions
{
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
params PointF[] points) =>
source.Fill(options, brush, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
IBrush brush,
params PointF[] points) =>
source.Fill(brush, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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)));
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
Color color,
params PointF[] points) =>
source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)));
}
}

66
src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the filling of rectangles to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class FillRectangleExtensions
{
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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));
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext
Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) =>
source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
RectangleF shape) =>
source.Fill(options, new SolidBrush(color), shape);
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext
Fill(this IImageProcessingContext source, Color color, RectangleF shape) =>
source.Fill(new SolidBrush(color), shape);
}
}

95
src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs

@ -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
{
/// <summary>
/// Adds extensions that allow the filling of regions with various brushes to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class FillRegionExtensions
{
/// <summary>
/// Flood fills the image with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The details how to fill the region of interest.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) =>
source.Fill(GraphicsOptions.Default, brush);
/// <summary>
/// Flood fills the image with the specified color.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color) =>
source.Fill(new SolidBrush(color));
/// <summary>
/// Flood fills the image with in the region with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) =>
source.Fill(GraphicsOptions.Default, brush, region);
/// <summary>
/// Flood fills the image with in the region with the specified color.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
Region region) =>
source.Fill(options, new SolidBrush(color), region);
/// <summary>
/// Flood fills the image with in the region with the specified color.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, Region region) =>
source.Fill(new SolidBrush(color), region);
/// <summary>
/// Flood fills the image with in the region with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
Region region) =>
source.ApplyProcessor(new FillRegionProcessor(brush, region, options));
/// <summary>
/// Flood fills the image with the specified brush.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The details how to fill the region of interest.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush) =>
source.ApplyProcessor(new FillProcessor(brush, options));
}
}

179
src/ImageSharp.Drawing/Processing/GradientBrush.cs

@ -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
{
/// <summary>
/// Base class for Gradient brushes
/// </summary>
public abstract class GradientBrush : IBrush
{
/// <inheritdoc cref="IBrush"/>
/// <param name="repetitionMode">Defines how the colors are repeated beyond the interval [0..1]</param>
/// <param name="colorStops">The gradient colors.</param>
protected GradientBrush(
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
{
this.RepetitionMode = repetitionMode;
this.ColorStops = colorStops;
}
/// <summary>
/// Gets how the colors are repeated beyond the interval [0..1].
/// </summary>
protected GradientRepetitionMode RepetitionMode { get; }
/// <summary>
/// Gets the list of color stops for this gradient.
/// </summary>
protected ColorStop[] ColorStops { get; }
/// <inheritdoc />
public abstract BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Base class for gradient brush applicators
/// </summary>
internal abstract class GradientBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private static readonly TPixel Transparent = Color.Transparent.ToPixel<TPixel>();
private readonly ColorStop[] colorStops;
private readonly GradientRepetitionMode repetitionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GradientBrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
/// <param name="colorStops">An array of color stops sorted by their position.</param>
/// <param name="repetitionMode">Defines if and how the gradient should be repeated.</param>
protected GradientBrushApplicator(
ImageFrame<TPixel> 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;
}
/// <summary>
/// Base implementation of the indexer for gradients
/// (follows the facade pattern, using abstract methods)
/// </summary>
/// <param name="x">X coordinate of the Pixel.</param>
/// <param name="y">Y coordinate of the Pixel.</param>
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<TPixel>();
}
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;
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="x">The x coordinate of the point</param>
/// <param name="y">The y coordinate of the point</param>
/// <returns>
/// 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 <see cref="GradientRepetitionMode" /> enum.
/// </returns>
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);
}
}
}
}

37
src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs

@ -1,37 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Modes to repeat a gradient.
/// </summary>
public enum GradientRepetitionMode
{
/// <summary>
/// don't repeat, keep the color of start and end beyond those points stable.
/// </summary>
None,
/// <summary>
/// Repeat the gradient.
/// If it's a black-white gradient, with Repeat it will be Black->{gray}->White|Black->{gray}->White|...
/// </summary>
Repeat,
/// <summary>
/// Reflect the gradient.
/// Similar to <see cref="Repeat"/>, but each other repetition uses inverse order of <see cref="ColorStop"/>s.
/// Used on a Black-White gradient, Reflect leads to Black->{gray}->White->{gray}->White...
/// </summary>
Reflect,
/// <summary>
/// With DontFill a gradient does not touch any pixel beyond it's borders.
/// For the <see cref="LinearGradientBrush" /> this is beyond the orthogonal through start and end,
/// TODO For the cref="PolygonalGradientBrush" it's outside the polygon,
/// For <see cref="RadialGradientBrush" /> and <see cref="EllipticGradientBrush" /> it's beyond 1.0.
/// </summary>
DontFill
}
}

38
src/ImageSharp.Drawing/Processing/IBrush.cs

@ -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
{
/// <summary>
/// Brush represents a logical configuration of a brush which can be used to source pixel colors
/// </summary>
/// <remarks>
/// A brush is a simple class that will return an <see cref="BrushApplicator{TPixel}" /> that will perform the
/// logic for retrieving pixel values for specific locations.
/// </remarks>
public interface IBrush
{
/// <summary>
/// Creates the applicator for this brush.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="source">The source image.</param>
/// <param name="region">The region the brush will be applied to.</param>
/// <param name="options">The graphic options</param>
/// <returns>
/// The brush applicator for this brush
/// </returns>
/// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would usually be the
/// bounding box of the shape not necessarily the bounds of the whole image
/// </remarks>
BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>;
}
}

28
src/ImageSharp.Drawing/Processing/IPen.cs

@ -1,28 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Interface representing the pattern and size of the stroke to apply with a Pen.
/// </summary>
public interface IPen
{
/// <summary>
/// Gets the stroke fill.
/// </summary>
IBrush StrokeFill { get; }
/// <summary>
/// Gets the width to apply to the stroke
/// </summary>
float StrokeWidth { get; }
/// <summary>
/// Gets the stoke pattern.
/// </summary>
ReadOnlySpan<float> StrokePattern { get; }
}
}

170
src/ImageSharp.Drawing/Processing/ImageBrush.cs

@ -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
{
/// <summary>
/// Provides an implementation of an image brush for painting images within areas.
/// </summary>
public class ImageBrush : IBrush
{
/// <summary>
/// The image to paint.
/// </summary>
private readonly Image image;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush"/> class.
/// </summary>
/// <param name="image">The image.</param>
public ImageBrush(Image image)
{
this.image = image;
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
if (this.image is Image<TPixel> specificImage)
{
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, false);
}
specificImage = this.image.CloneAs<TPixel>();
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, true);
}
/// <summary>
/// The image brush applicator.
/// </summary>
private class ImageBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private ImageFrame<TPixel> sourceFrame;
private Image<TPixel> sourceImage;
private readonly bool shouldDisposeImage;
/// <summary>
/// The y-length.
/// </summary>
private readonly int yLength;
/// <summary>
/// The x-length.
/// </summary>
private readonly int xLength;
/// <summary>
/// The Y offset.
/// </summary>
private readonly int offsetY;
/// <summary>
/// The X offset.
/// </summary>
private readonly int offsetX;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="target">The target image.</param>
/// <param name="image">The image.</param>
/// <param name="region">The region.</param>
/// <param name="options">The options</param>
/// <param name="shouldDisposeImage">Whether to dispose the image on disposal of the applicator.</param>
public ImageBrushApplicator(
ImageFrame<TPixel> target,
Image<TPixel> 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);
}
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
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];
}
}
/// <inheritdoc />
public override void Dispose()
{
if (this.shouldDisposeImage)
{
this.sourceImage?.Dispose();
this.sourceImage = null;
this.sourceFrame = null;
}
}
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
// Create a span for colors
using (IMemoryOwner<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
Span<TPixel> 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<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(
this.sourceFrame.Configuration,
destinationRow,
destinationRow,
overlaySpan,
amountSpan);
}
}
}
}
}

161
src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs

@ -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
{
/// <summary>
/// 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.
/// </summary>
public sealed class LinearGradientBrush : GradientBrush
{
private readonly PointF p1;
private readonly PointF p2;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrush"/> class.
/// </summary>
/// <param name="p1">Start point</param>
/// <param name="p2">End point</param>
/// <param name="repetitionMode">defines how colors are repeated.</param>
/// <param name="colorStops"><inheritdoc /></param>
public LinearGradientBrush(
PointF p1,
PointF p2,
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
{
this.p1 = p1;
this.p2 = p2;
}
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new LinearGradientBrushApplicator<TPixel>(
source,
this.p1,
this.p2,
this.ColorStops,
this.RepetitionMode,
options);
/// <summary>
/// The linear gradient brush applicator.
/// </summary>
private sealed class LinearGradientBrushApplicator<TPixel> : GradientBrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PointF start;
private readonly PointF end;
/// <summary>
/// the vector along the gradient, x component
/// </summary>
private readonly float alongX;
/// <summary>
/// the vector along the gradient, y component
/// </summary>
private readonly float alongY;
/// <summary>
/// the vector perpendicular to the gradient, y component
/// </summary>
private readonly float acrossY;
/// <summary>
/// the vector perpendicular to the gradient, x component
/// </summary>
private readonly float acrossX;
/// <summary>
/// the result of <see cref="alongX"/>^2 + <see cref="alongY"/>^2
/// </summary>
private readonly float alongsSquared;
/// <summary>
/// the length of the defined gradient (between source and end)
/// </summary>
private readonly float length;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="source">The source</param>
/// <param name="start">start point of the gradient</param>
/// <param name="end">end point of the gradient</param>
/// <param name="colorStops">tuple list of colors and their respective position between 0 and 1 on the line</param>
/// <param name="repetitionMode">defines how the gradient colors are repeated.</param>
/// <param name="options">the graphics options</param>
public LinearGradientBrushApplicator(
ImageFrame<TPixel> 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()
{
}
}
}
}

287
src/ImageSharp.Drawing/Processing/PathGradientBrush.cs

@ -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
{
/// <summary>
/// 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.
/// </summary>
public sealed class PathGradientBrush : IBrush
{
private readonly IList<Edge> edges;
private readonly Color centerColor;
/// <summary>
/// Initializes a new instance of the <see cref="PathGradientBrush"/> class.
/// </summary>
/// <param name="points">Points that constitute a polygon that represents the gradient area.</param>
/// <param name="colors">Array of colors that correspond to each point in the polygon.</param>
/// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param>
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();
}
/// <summary>
/// Initializes a new instance of the <see cref="PathGradientBrush"/> class.
/// </summary>
/// <param name="points">Points that constitute a polygon that represents the gradient area.</param>
/// <param name="colors">Array of colors that correspond to each point in the polygon.</param>
public PathGradientBrush(PointF[] points, Color[] colors)
: this(points, colors, CalculateCenterColor(colors))
{
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return new PathGradientBrushApplicator<TPixel>(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; }
}
/// <summary>
/// An edge of the polygon that represents the gradient area.
/// </summary>
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));
}
/// <summary>
/// The path gradient brush applicator.
/// </summary>
private class PathGradientBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PointF center;
private readonly Vector4 centerColor;
private readonly float maxDistance;
private readonly IList<Edge> edges;
/// <summary>
/// Initializes a new instance of the <see cref="PathGradientBrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="edges">Edges of the polygon.</param>
/// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param>
/// <param name="options">The options.</param>
public PathGradientBrushApplicator(
ImageFrame<TPixel> source,
IList<Edge> 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();
}
/// <inheritdoc />
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<TPixel>();
}
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<TPixel>();
}
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<TPixel>();
}
}
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;
}
/// <inheritdoc />
public override void Dispose()
{
}
}
}
}

184
src/ImageSharp.Drawing/Processing/PatternBrush.cs

@ -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
{
/// <summary>
/// Provides an implementation of a pattern brush for painting patterns.
/// </summary>
/// <remarks>
/// 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
/// <para>
/// 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
/// </para>
/// <para>
/// or you want a horizontal stripe which is 3 pixels apart you would use a pattern like
/// 1
/// 0
/// 0
/// </para>
/// </remarks>
public class PatternBrush : IBrush
{
/// <summary>
/// The pattern.
/// </summary>
private readonly DenseMatrix<Color> pattern;
private readonly DenseMatrix<Vector4> patternVector;
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush"/> class.
/// </summary>
/// <param name="foreColor">Color of the fore.</param>
/// <param name="backColor">Color of the back.</param>
/// <param name="pattern">The pattern.</param>
public PatternBrush(Color foreColor, Color backColor, bool[,] pattern)
: this(foreColor, backColor, new DenseMatrix<bool>(pattern))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush"/> class.
/// </summary>
/// <param name="foreColor">Color of the fore.</param>
/// <param name="backColor">Color of the back.</param>
/// <param name="pattern">The pattern.</param>
internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix<bool> pattern)
{
var foreColorVector = foreColor.ToVector4();
var backColorVector = backColor.ToVector4();
this.pattern = new DenseMatrix<Color>(pattern.Columns, pattern.Rows);
this.patternVector = new DenseMatrix<Vector4>(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;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
internal PatternBrush(PatternBrush brush)
{
this.pattern = brush.pattern;
this.patternVector = brush.patternVector;
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> =>
new PatternBrushApplicator<TPixel>(
source,
this.pattern.ToPixelMatrix<TPixel>(source.Configuration),
options);
/// <summary>
/// The pattern brush applicator.
/// </summary>
private class PatternBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The pattern.
/// </summary>
private readonly DenseMatrix<TPixel> pattern;
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="options">The options</param>
public PatternBrushApplicator(ImageFrame<TPixel> source, in DenseMatrix<TPixel> pattern, GraphicsOptions options)
: base(source, options)
{
this.pattern = pattern;
}
/// <summary>
/// Gets the color for a single pixel.
/// </summary>#
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The Color.
/// </returns>
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];
}
}
/// <inheritdoc />
public override void Dispose()
{
// noop
}
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
int patternY = y % this.pattern.Rows;
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> 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<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(
this.Target.Configuration,
destinationRow,
destinationRow,
overlaySpan,
amountSpan);
}
}
}
}
}

76
src/ImageSharp.Drawing/Processing/Pen.cs

@ -1,76 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides a pen that can apply a pattern to a line with a set brush and thickness
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public class Pen : IPen
{
private readonly float[] pattern;
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <param name="pattern">The pattern.</param>
public Pen(Color color, float width, float[] pattern)
: this(new SolidBrush(color), width, pattern)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <param name="pattern">The pattern.</param>
public Pen(IBrush brush, float width, float[] pattern)
{
this.StrokeFill = brush;
this.StrokeWidth = width;
this.pattern = pattern;
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
public Pen(Color color, float width)
: this(new SolidBrush(color), width)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
public Pen(IBrush brush, float width)
: this(brush, width, Pens.EmptyPattern)
{
}
/// <inheritdoc/>
public IBrush StrokeFill { get; }
/// <inheritdoc/>
public float StrokeWidth { get; }
/// <inheritdoc/>
public ReadOnlySpan<float> StrokePattern => this.pattern;
}
}

97
src/ImageSharp.Drawing/Processing/Pens.cs

@ -1,97 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Contains a collection of common Pen styles
/// </summary>
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];
/// <summary>
/// Create a solid pen with out any drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen Solid(Color color, float width) => new Pen(color, width);
/// <summary>
/// Create a solid pen with out any drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen Solid(IBrush brush, float width) => new Pen(brush, width);
/// <summary>
/// Create a pen with a 'Dash' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen Dash(Color color, float width) => new Pen(color, width, DashedPattern);
/// <summary>
/// Create a pen with a 'Dash' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen Dash(IBrush brush, float width) => new Pen(brush, width, DashedPattern);
/// <summary>
/// Create a pen with a 'Dot' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen Dot(Color color, float width) => new Pen(color, width, DottedPattern);
/// <summary>
/// Create a pen with a 'Dot' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen Dot(IBrush brush, float width) => new Pen(brush, width, DottedPattern);
/// <summary>
/// Create a pen with a 'Dash Dot' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen DashDot(Color color, float width) => new Pen(color, width, DashDotPattern);
/// <summary>
/// Create a pen with a 'Dash Dot' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen DashDot(IBrush brush, float width) => new Pen(brush, width, DashDotPattern);
/// <summary>
/// Create a pen with a 'Dash Dot Dot' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen DashDotDot(Color color, float width) => new Pen(color, width, DashDotDotPattern);
/// <summary>
/// Create a pen with a 'Dash Dot Dot' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <returns>The Pen</returns>
public static Pen DashDotDot(IBrush brush, float width) => new Pen(brush, width, DashDotDotPattern);
}
}

101
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -1,101 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
public class DrawImageProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary>
/// <param name="image">The image to blend.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend.</param>
public DrawImageProcessor(
Image image,
Point location,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
{
this.Image = image;
this.Location = location;
this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity;
}
/// <summary>
/// Gets the image to blend.
/// </summary>
public Image Image { get; }
/// <summary>
/// Gets the location to draw the blended image.
/// </summary>
public Point Location { get; }
/// <summary>
/// Gets the blending mode to use when drawing the image.
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; }
/// <summary>
/// Gets the Alpha blending mode to use when drawing the image.
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; }
/// <summary>
/// Gets the opacity of the image to blend.
/// </summary>
public float Opacity { get; }
/// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Image<TPixelBg> source, Rectangle sourceRectangle)
where TPixelBg : struct, IPixel<TPixelBg>
{
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this, source, sourceRectangle);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
private class ProcessorFactoryVisitor<TPixelBg> : IImageVisitor
where TPixelBg : struct, IPixel<TPixelBg>
{
private readonly DrawImageProcessor definition;
private readonly Image<TPixelBg> source;
private readonly Rectangle sourceRectangle;
public ProcessorFactoryVisitor(DrawImageProcessor definition, Image<TPixelBg> source, Rectangle sourceRectangle)
{
this.definition = definition;
this.source = source;
this.sourceRectangle = sourceRectangle;
}
public IImageProcessor<TPixelBg> Result { get; private set; }
public void Visit<TPixelFg>(Image<TPixelFg> image)
where TPixelFg : struct, IPixel<TPixelFg>
{
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
image,
this.source,
this.sourceRectangle,
this.definition.Location,
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
this.definition.Opacity);
}
}
}
}

115
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -1,115 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="TPixelBg">The pixel format of destination image.</typeparam>
/// <typeparam name="TPixelFg">The pixel format of source image.</typeparam>
internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
where TPixelBg : struct, IPixel<TPixelBg>
where TPixelFg : struct, IPixel<TPixelFg>
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class.
/// </summary>
/// <param name="image">The foreground <see cref="Image{TPixelFg}"/> to blend with the currently processing image.</param>
/// <param name="source">The source <see cref="Image{TPixelBg}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(
Image<TPixelFg> image,
Image<TPixelBg> source,
Rectangle sourceRectangle,
Point location,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
: base(source, sourceRectangle)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image;
this.Opacity = opacity;
this.Blender = PixelOperations<TPixelBg>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location;
}
/// <summary>
/// Gets the image to blend
/// </summary>
public Image<TPixelFg> Image { get; }
/// <summary>
/// Gets the opacity of the image to blend
/// </summary>
public float Opacity { get; }
/// <summary>
/// Gets the pixel blender
/// </summary>
public PixelBlender<TPixelBg> Blender { get; }
/// <summary>
/// Gets the location to draw the blended image
/// </summary>
public Point Location { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelBg> source)
{
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
Image<TPixelFg> targetImage = this.Image;
PixelBlender<TPixelBg> blender = this.Blender;
int locationY = this.Location.Y;
// Align start/end positions.
Rectangle bounds = targetImage.Bounds();
int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right);
int targetX = minX - this.Location.X;
int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
int width = maxX - minX;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
// Not a valid operation because rectangle does not overlap with this image.
if (workingRect.Width <= 0 || workingRect.Height <= 0)
{
throw new ImageProcessingException(
"Cannot draw image because the source image does not overlap the target image.");
}
ParallelHelper.IterateRows(
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixelBg> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelFg> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelFg>(configuration, background, background, foreground, this.Opacity);
}
});
}
}
}

43
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -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
{
/// <summary>
/// Defines a processor to fill an <see cref="Image"/> with the given <see cref="IBrush"/>
/// using blending defined by the given <see cref="GraphicsOptions"/>.
/// </summary>
public class FillProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="FillProcessor"/> class.
/// </summary>
/// <param name="brush">The brush to use for filling.</param>
/// <param name="options">The <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.</param>
public FillProcessor(IBrush brush, GraphicsOptions options)
{
this.Brush = brush;
this.Options = options;
}
/// <summary>
/// Gets the <see cref="IBrush"/> used for filling the destination image.
/// </summary>
public IBrush Brush { get; }
/// <summary>
/// Gets the <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.
/// </summary>
public GraphicsOptions Options { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new FillProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

121
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs

@ -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
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly FillProcessor definition;
public FillProcessor(FillProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> 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<TPixel>();
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<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> 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);
}
}
}

51
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -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
{
/// <summary>
/// Defines a processor to fill <see cref="Image"/> pixels withing a given <see cref="Region"/>
/// with the given <see cref="IBrush"/> and blending defined by the given <see cref="GraphicsOptions"/>.
/// </summary>
public class FillRegionProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="FillRegionProcessor" /> class.
/// </summary>
/// <param name="brush">The details how to fill the region of interest.</param>
/// <param name="region">The region of interest to be filled.</param>
/// <param name="options">The configuration options.</param>
public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options)
{
this.Region = region;
this.Brush = brush;
this.Options = options;
}
/// <summary>
/// Gets the <see cref="IBrush"/> used for filling the destination image.
/// </summary>
public IBrush Brush { get; }
/// <summary>
/// Gets the region that this processor applies to.
/// </summary>
public Region Region { get; }
/// <summary>
/// Gets the <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.
/// </summary>
public GraphicsOptions Options { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new FillRegionProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

197
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs

@ -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
{
/// <summary>
/// Using a brush and a shape fills shape with contents of brush the
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <seealso cref="ImageProcessor{TPixel}" />
internal class FillRegionProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly FillRegionProcessor definition;
public FillRegionProcessor(FillRegionProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> 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<TPixel> applicator = brush.CreateApplicator(source, rect, options))
{
int scanlineWidth = maxX - minX;
using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
using (IMemoryOwner<float> bScanline = source.MemoryAllocator.Allocate<float>(scanlineWidth))
{
bool scanlineDirty = true;
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush);
TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel<TPixel>() : 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);
}
}
}

81
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs

@ -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
{
/// <summary>
/// Defines a processor to draw text on an <see cref="Image"/>.
/// </summary>
public class DrawTextProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawTextProcessor"/> class.
/// </summary>
/// <param name="options">The options</param>
/// <param name="text">The text we want to render</param>
/// <param name="font">The font we want to render with</param>
/// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="pen">The pen to outline text with.</param>
/// <param name="location">The location on the image to start drawing the text from.</param>
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;
}
/// <summary>
/// Gets the brush used to fill the glyphs.
/// </summary>
public IBrush Brush { get; }
/// <summary>
/// Gets the <see cref="TextGraphicsOptions"/> defining blending modes and text-specific drawing settings.
/// </summary>
public TextGraphicsOptions Options { get; }
/// <summary>
/// Gets the text to draw.
/// </summary>
public string Text { get; }
/// <summary>
/// Gets the pen used for outlining the text, if Null then we will not outline
/// </summary>
public IPen Pen { get; }
/// <summary>
/// Gets the font used to render the text.
/// </summary>
public Font Font { get; }
/// <summary>
/// Gets the location to draw the text at.
/// </summary>
public PointF Location { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new DrawTextProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

453
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs

@ -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
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DrawTextProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private CachingGlyphRenderer textRenderer;
private readonly DrawTextProcessor definition;
public DrawTextProcessor(DrawTextProcessor definition, Image<TPixel> 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;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> 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<DrawingOperation> operations, IBrush brush)
{
if (operations?.Count > 0)
{
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, this.SourceRectangle, this.textRenderer.Options))
{
foreach (DrawingOperation operation in operations)
{
Buffer2D<float> 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<float> span = buffer.GetRowSpan(row).Slice(offsetSpan);
app.Apply(span, startX, y);
}
}
}
}
}
}
private struct DrawingOperation
{
public Buffer2D<float> 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<DrawingOperation>(size);
}
if (this.renderOutline)
{
this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2);
this.OutlineOperations = new List<DrawingOperation>(size);
}
this.builder = new PathBuilder();
}
public List<DrawingOperation> FillOperations { get; }
public List<DrawingOperation> 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<float> 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<float> fullBuffer = this.MemoryAllocator.Allocate2D<float>(size.Width + 1, size.Height + 1, AllocationOptions.Clean);
using (IMemoryOwner<float> bufferBacking = this.MemoryAllocator.Allocate<float>(path.MaxIntersections))
using (IMemoryOwner<PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate<PointF>(size.Width))
{
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (int y = 0; y <= size.Height; y++)
{
Span<float> 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<PointF> intersectionSpan = rowIntersectionBuffer.GetSpan();
Span<float> 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<float> FillMap;
public Buffer2D<float> OutlineMap;
public void Dispose()
{
this.FillMap?.Dispose();
this.OutlineMap?.Dispose();
}
}
}
}
}

104
src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs

@ -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
{
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.
/// </summary>
public sealed class RadialGradientBrush : GradientBrush
{
private readonly PointF center;
private readonly float radius;
/// <inheritdoc cref="GradientBrush" />
/// <param name="center">The center of the circular gradient and 0 for the color stops.</param>
/// <param name="radius">The radius of the circular gradient and 1 for the color stops.</param>
/// <param name="repetitionMode">Defines how the colors in the gradient are repeated.</param>
/// <param name="colorStops">the color stops as defined in base class.</param>
public RadialGradientBrush(
PointF center,
float radius,
GradientRepetitionMode repetitionMode,
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator<TPixel>(
source,
options,
this.center,
this.radius,
this.ColorStops,
this.RepetitionMode);
/// <inheritdoc />
private sealed class RadialGradientBrushApplicator<TPixel> : GradientBrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PointF center;
private readonly float radius;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options.</param>
/// <param name="center">Center point of the gradient.</param>
/// <param name="radius">Radius of the gradient.</param>
/// <param name="colorStops">Definition of colors.</param>
/// <param name="repetitionMode">How the colors are repeated beyond the first gradient.</param>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> target,
GraphicsOptions options,
PointF center,
float radius,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="Dispose" />
public override void Dispose()
{
}
/// <summary>
/// As this is a circular gradient, the position on the gradient is based on
/// the distance of the point to the center.
/// </summary>
/// <param name="x">The X coordinate of the target pixel.</param>
/// <param name="y">The Y coordinate of the target pixel.</param>
/// <returns>the position on the color gradient.</returns>
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<float> 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);
}
}
}
}

179
src/ImageSharp.Drawing/Processing/RecolorBrush.cs

@ -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
{
/// <summary>
/// Provides an implementation of a brush that can recolor an image
/// </summary>
public class RecolorBrush : IBrush
{
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrush" /> class.
/// </summary>
/// <param name="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold as a value between 0 and 1.</param>
public RecolorBrush(Color sourceColor, Color targetColor, float threshold)
{
this.SourceColor = sourceColor;
this.Threshold = threshold;
this.TargetColor = targetColor;
}
/// <summary>
/// Gets the threshold.
/// </summary>
public float Threshold { get; }
/// <summary>
/// Gets the source color.
/// </summary>
/// <value>
/// The color of the source.
/// </value>
public Color SourceColor { get; }
/// <summary>
/// Gets the target color.
/// </summary>
public Color TargetColor { get; }
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return new RecolorBrushApplicator<TPixel>(
source,
this.SourceColor.ToPixel<TPixel>(),
this.TargetColor.ToPixel<TPixel>(),
this.Threshold,
options);
}
/// <summary>
/// The recolor brush applicator.
/// </summary>
private class RecolorBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The source color.
/// </summary>
private readonly Vector4 sourceColor;
/// <summary>
/// The target color.
/// </summary>
private readonly Vector4 targetColor;
/// <summary>
/// The threshold.
/// </summary>
private readonly float threshold;
private readonly TPixel targetColorPixel;
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param>
/// <param name="options">The options</param>
public RecolorBrushApplicator(ImageFrame<TPixel> 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;
}
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
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;
}
}
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> 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<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(
this.Target.Configuration,
destinationRow,
destinationRow,
overlaySpan,
amountSpan);
}
}
}
}
}

133
src/ImageSharp.Drawing/Processing/SolidBrush.cs

@ -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
{
/// <summary>
/// Provides an implementation of a solid brush for painting solid color areas.
/// </summary>
public class SolidBrush : IBrush
{
/// <summary>
/// The color to paint.
/// </summary>
private readonly Color color;
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrush"/> class.
/// </summary>
/// <param name="color">The color.</param>
public SolidBrush(Color color)
{
this.color = color;
}
/// <summary>
/// Gets the color.
/// </summary>
/// <value>
/// The color.
/// </value>
public Color Color => this.color;
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return new SolidBrushApplicator<TPixel>(source, this.color.ToPixel<TPixel>(), options);
}
/// <summary>
/// The solid brush applicator.
/// </summary>
private class SolidBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="color">The color.</param>
/// <param name="options">The options</param>
public SolidBrushApplicator(ImageFrame<TPixel> source, TPixel color, GraphicsOptions options)
: base(source, options)
{
this.Colors = source.MemoryAllocator.Allocate<TPixel>(source.Width);
this.Colors.GetSpan().Fill(color);
}
/// <summary>
/// Gets the colors.
/// </summary>
protected IMemoryOwner<TPixel> Colors { get; }
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] => this.Colors.GetSpan()[x];
/// <inheritdoc />
public override void Dispose()
{
this.Colors.Dispose();
}
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
Span<TPixel> 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<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
{
Span<float> 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);
}
}
}
}
}
}

163
src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs

@ -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
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct TextGraphicsOptions
{
private const int DefaultTextDpi = 72;
/// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
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;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; }
/// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary>
public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; }
/// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary>
public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; }
// In the future we could expose a PixelBlender<TPixel> directly on here
// or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1.
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; set; }
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
/// </summary>
public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; }
/// <summary>
/// Gets or sets a value indicating the number of space widths a tab should lock to.
/// </summary>
public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; }
/// <summary>
/// Gets or sets a value indicating if greater than zero determine the width at which text should wrap.
/// </summary>
public float WrapTextWidth { get; set; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the X axis.
/// </summary>
public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the Y axis.
/// </summary>
public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; }
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// If <see cref="WrapTextWidth"/> is greater than zero it will align relative to the space
/// defined by the location and width, if <see cref="WrapTextWidth"/> equals zero, and thus
/// wrapping disabled, then the alignment is relative to the drawing location.
/// </summary>
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; }
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; }
/// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{
return new TextGraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage,
ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode
};
}
/// <summary>
/// Performs an explicit conversion from <see cref="TextGraphicsOptions"/> to <see cref="GraphicsOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{
return new GraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode,
BlendPercentage = options.BlendPercentage
};
}
}
}

84
src/ImageSharp.Drawing/Utils/QuickSort.cs

@ -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
{
/// <summary>
/// Optimized quick sort implementation for Span{float} input
/// </summary>
internal class QuickSort
{
/// <summary>
/// Sorts the elements of <paramref name="data"/> in ascending order
/// </summary>
/// <param name="data">The items to sort</param>
public static void Sort(Span<float> 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));
}
}
}
}

164
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -2,11 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Advanced
{
@ -30,105 +30,53 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source image.</param>
/// <returns>Returns the configuration.</returns>
public static Configuration GetConfiguration(this Image source)
=> GetConfiguration((IConfigurable)source);
=> GetConfiguration((IConfigurationProvider)source);
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// Gets the configuration for the image frame.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelSpan<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.GetPixelMemory().Span;
/// <param name="source">The source image.</param>
/// <returns>Returns the configuration.</returns>
public static Configuration GetConfiguration(this ImageFrame source)
=> GetConfiguration((IConfigurationProvider)source);
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// Gets the configuration.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelSpan<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelSpan();
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
private static Configuration GetConfiguration(IConfigurationProvider source)
=> source?.Configuration ?? Configuration.Default;
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// Gets the representation of the pixels as a <see cref="IMemoryGroup{T}"/> containing the backing pixel data of the image
/// stored in row major order, as a list of contiguous <see cref="Memory{T}"/> blocks in the source image's pixel format.
/// </summary>
/// <param name="source">The source image.</param>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelRowSpan<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetRowSpan(rowIndex);
/// <returns>The <see cref="IMemoryGroup{T}"/>.</returns>
/// <remarks>
/// Certain Image Processors may invalidate the returned <see cref="IMemoryGroup{T}"/> and all it's buffers,
/// therefore it's not recommended to mutate the image while holding a reference to it's <see cref="IMemoryGroup{T}"/>.
/// </remarks>
public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this ImageFrame<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
=> source?.PixelBuffer.FastMemoryGroup.View ?? throw new ArgumentNullException(nameof(source));
/// <summary>
/// Gets the representation of the pixels as <see cref="Span{T}"/> of of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// Gets the representation of the pixels as a <see cref="IMemoryGroup{T}"/> containing the backing pixel data of the image
/// stored in row major order, as a list of contiguous <see cref="Memory{T}"/> blocks in the source image's pixel format.
/// </summary>
/// <param name="source">The source image.</param>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
public static Span<TPixel> GetPixelRowSpan<TPixel>(this Image<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(rowIndex);
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer,
/// allowing direct manipulation of pixel data through unsafe operations.
/// The pixel buffer is a contiguous memory area containing Width*Height TPixel elements laid out in row-major order.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image frame</param>
/// <returns>A pinnable reference the first root of the pixel buffer.</returns>
[Obsolete("This method will be removed in our next release! Please use MemoryMarshal.GetReference(source.GetPixelSpan())!")]
public static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref DangerousGetPinnableReferenceToPixelBuffer((IPixelSource<TPixel>)source);
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer,
/// allowing direct manipulation of pixel data through unsafe operations.
/// The pixel buffer is a contiguous memory area containing Width*Height TPixel elements laid out in row-major order.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>A pinnable reference the first root of the pixel buffer.</returns>
[Obsolete("This method will be removed in our next release! Please use MemoryMarshal.GetReference(source.GetPixelSpan())!")]
public static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref source.Frames.RootFrame.DangerousGetPinnableReferenceToPixelBuffer();
/// <summary>
/// Gets the representation of the pixels as a <see cref="Memory{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source <see cref="ImageFrame{TPixel}"/></param>
/// <returns>The <see cref="Memory{T}"/></returns>
internal static Memory<TPixel> GetPixelMemory<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
return source.PixelBuffer.MemorySource.Memory;
}
/// <summary>
/// Gets the representation of the pixels as a <see cref="Memory{T}"/> of contiguous memory in the source image's pixel format
/// stored in row major order.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source <see cref="Image{TPixel}"/></param>
/// <returns>The <see cref="Memory{T}"/></returns>
internal static Memory<TPixel> GetPixelMemory<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
return source.Frames.RootFrame.GetPixelMemory();
}
/// <returns>The <see cref="IMemoryGroup{T}"/>.</returns>
/// <remarks>
/// Certain Image Processors may invalidate the returned <see cref="IMemoryGroup{T}"/> and all it's buffers,
/// therefore it's not recommended to mutate the image while holding a reference to it's <see cref="IMemoryGroup{T}"/>.
/// </remarks>
public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this Image<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
=> source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException(nameof(source));
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
@ -138,9 +86,15 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
internal static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetRowMemory(rowIndex);
public static Memory<TPixel> GetPixelRowMemory<TPixel>(this ImageFrame<TPixel> source, int rowIndex)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
Guard.MustBeLessThan(rowIndex, source.Height, nameof(rowIndex));
return source.PixelBuffer.GetSafeRowMemory(rowIndex);
}
/// <summary>
/// Gets the representation of the pixels as <see cref="Span{T}"/> of of contiguous memory
@ -150,34 +104,22 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="rowIndex">The row.</param>
/// <returns>The <see cref="Span{TPixel}"/></returns>
internal static Memory<TPixel> GetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowMemory(rowIndex);
public static Memory<TPixel> GetPixelRowMemory<TPixel>(this Image<TPixel> source, int rowIndex)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
Guard.MustBeLessThan(rowIndex, source.Height, nameof(rowIndex));
return source.Frames.RootFrame.PixelBuffer.GetSafeRowMemory(rowIndex);
}
/// <summary>
/// Gets the <see cref="MemoryAllocator"/> assigned to 'source'.
/// </summary>
/// <param name="source">The source image.</param>
/// <returns>Returns the configuration.</returns>
internal static MemoryAllocator GetMemoryAllocator(this IConfigurable source)
internal static MemoryAllocator GetMemoryAllocator(this IConfigurationProvider source)
=> GetConfiguration(source).MemoryAllocator;
/// <summary>
/// Gets the configuration.
/// </summary>
/// <param name="source">The source image</param>
/// <returns>Returns the bounds of the image</returns>
private static Configuration GetConfiguration(IConfigurable source)
=> source?.Configuration ?? Configuration.Default;
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer.
/// Such a reference can be used for pinning but must never be dereferenced.
/// </summary>
/// <param name="source">The source image frame</param>
/// <returns>A reference to the element.</returns>
private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref MemoryMarshal.GetReference(source.PixelBuffer.GetSpan());
}
}

56
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -1,11 +1,14 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -19,6 +22,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.)
/// </summary>
[ExcludeFromCodeCoverage]
internal static class AotCompilerTools
{
static AotCompilerTools()
@ -40,7 +44,7 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
private static void SeedEverything()
{
Seed<Alpha8>();
Seed<A8>();
Seed<Argb32>();
Seed<Bgr24>();
Seed<Bgr565>();
@ -48,8 +52,10 @@ namespace SixLabors.ImageSharp.Advanced
Seed<Bgra4444>();
Seed<Bgra5551>();
Seed<Byte4>();
Seed<Gray16>();
Seed<Gray8>();
Seed<L16>();
Seed<L8>();
Seed<La16>();
Seed<La32>();
Seed<HalfSingle>();
Seed<HalfVector2>();
Seed<HalfVector4>();
@ -73,11 +79,12 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void Seed<TPixel>()
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
// This is we actually call all the individual methods you need to seed.
AotCompileOctreeQuantizer<TPixel>();
AotCompileWuQuantizer<TPixel>();
AotCompilePaletteQuantizer<TPixel>();
AotCompileDithering<TPixel>();
AotCompilePixelOperations<TPixel>();
@ -103,11 +110,12 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileOctreeQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
using (var test = new OctreeFrameQuantizer<TPixel>(new OctreeQuantizer(false)))
using (var test = new OctreeFrameQuantizer<TPixel>(Configuration.Default, new OctreeQuantizer().Options))
{
test.AotGetPalette();
var frame = new ImageFrame<TPixel>(Configuration.Default, 1, 1);
test.QuantizeFrame(frame, frame.Bounds());
}
}
@ -116,12 +124,26 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileWuQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
using (var test = new WuFrameQuantizer<TPixel>(Configuration.Default.MemoryAllocator, new WuQuantizer(false)))
using (var test = new WuFrameQuantizer<TPixel>(Configuration.Default, new WuQuantizer().Options))
{
test.QuantizeFrame(new ImageFrame<TPixel>(Configuration.Default, 1, 1));
test.AotGetPalette();
var frame = new ImageFrame<TPixel>(Configuration.Default, 1, 1);
test.QuantizeFrame(frame, frame.Bounds());
}
}
/// <summary>
/// This method pre-seeds the PaletteQuantizer in the AoT compiler for iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompilePaletteQuantizer<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
using (var test = (PaletteFrameQuantizer<TPixel>)new PaletteQuantizer(Array.Empty<Color>()).CreateFrameQuantizer<TPixel>(Configuration.Default))
{
var frame = new ImageFrame<TPixel>(Configuration.Default, 1, 1);
test.QuantizeFrame(frame, frame.Bounds());
}
}
@ -130,13 +152,15 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileDithering<TPixel>()
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
var test = new FloydSteinbergDiffuser();
ErrorDither errorDither = ErrorDither.FloydSteinberg;
OrderedDither orderedDither = OrderedDither.Bayer2x2;
TPixel pixel = default;
using (var image = new ImageFrame<TPixel>(Configuration.Default, 1, 1))
{
test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0, 0);
errorDither.Dither(image, image.Bounds(), pixel, pixel, 0, 0, 0);
orderedDither.Dither(pixel, 0, 0, 0, 0);
}
}
@ -147,7 +171,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="encoder">The image encoder to seed.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCodec<TPixel>(IImageDecoder decoder, IImageEncoder encoder)
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
try
{
@ -171,7 +195,7 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompilePixelOperations<TPixel>()
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
var pixelOp = new PixelOperations<TPixel>();
pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear);

16
src/ImageSharp/Advanced/IConfigurable.cs

@ -1,16 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Encapsulates the properties for configuration.
/// </summary>
internal interface IConfigurable
{
/// <summary>
/// Gets the configuration.
/// </summary>
Configuration Configuration { get; }
}
}

16
src/ImageSharp/Advanced/IConfigurationProvider.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for objects that can provide access to configuration.
/// </summary>
internal interface IConfigurationProvider
{
/// <summary>
/// Gets the configuration which allows altering default behaviour or extending the library.
/// </summary>
Configuration Configuration { get; }
}
}

2
src/ImageSharp/Advanced/IImageVisitor.cs

@ -17,6 +17,6 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="image">The image.</param>
/// <typeparam name="TPixel">The pixel type.</typeparam>
void Visit<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>;
where TPixel : unmanaged, IPixel<TPixel>;
}
}

17
src/ImageSharp/Advanced/IPixelSource.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.Memory;
@ -6,16 +6,27 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
internal interface IPixelSource
{
/// <summary>
/// Gets the pixel buffer.
/// </summary>
Buffer2D<byte> PixelBuffer { get; }
}
/// <summary>
/// Encapsulates the basic properties and methods required to manipulate images.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal interface IPixelSource<TPixel>
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Gets the pixel buffer.
/// </summary>
Buffer2D<TPixel> PixelBuffer { get; }
}
}
}

19
src/ImageSharp/Advanced/IRowIntervalOperation.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row interval.
/// </summary>
public interface IRowIntervalOperation
{
/// <summary>
/// Invokes the method passing the row interval.
/// </summary>
/// <param name="rows">The row interval.</param>
void Invoke(in RowInterval rows);
}
}

23
src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row interval with a temporary buffer.
/// </summary>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
public interface IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Invokes the method passing the row interval and a buffer.
/// </summary>
/// <param name="rows">The row interval.</param>
/// <param name="span">The contiguous region of memory.</param>
void Invoke(in RowInterval rows, Span<TBuffer> span);
}
}

17
src/ImageSharp/Advanced/IRowOperation.cs

@ -0,0 +1,17 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row.
/// </summary>
public interface IRowOperation
{
/// <summary>
/// Invokes the method passing the row y coordinate.
/// </summary>
/// <param name="y">The row y coordinate.</param>
void Invoke(int y);
}
}

22
src/ImageSharp/Advanced/IRowOperation{TBuffer}.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines the contract for an action that operates on a row with a temporary buffer.
/// </summary>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
public interface IRowOperation<TBuffer>
where TBuffer : unmanaged
{
/// <summary>
/// Invokes the method passing the row and a buffer.
/// </summary>
/// <param name="y">The row y coordinate.</param>
/// <param name="span">The contiguous region of memory.</param>
void Invoke(int y, Span<TBuffer> span);
}
}

101
src/ImageSharp/Advanced/ParallelExecutionSettings.cs

@ -0,0 +1,101 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Defines execution settings for methods in <see cref="ParallelRowIterator"/>.
/// </summary>
public readonly struct ParallelExecutionSettings
{
/// <summary>
/// Default value for <see cref="MinimumPixelsProcessedPerTask"/>.
/// </summary>
public const int DefaultMinimumPixelsProcessedPerTask = 4096;
/// <summary>
/// Initializes a new instance of the <see cref="ParallelExecutionSettings"/> struct.
/// </summary>
/// <param name="maxDegreeOfParallelism">The value used for initializing <see cref="ParallelOptions.MaxDegreeOfParallelism"/> when using TPL.</param>
/// <param name="minimumPixelsProcessedPerTask">The value for <see cref="MinimumPixelsProcessedPerTask"/>.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/>.</param>
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;
}
/// <summary>
/// Initializes a new instance of the <see cref="ParallelExecutionSettings"/> struct.
/// </summary>
/// <param name="maxDegreeOfParallelism">The value used for initializing <see cref="ParallelOptions.MaxDegreeOfParallelism"/> when using TPL.</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/>.</param>
public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator)
: this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator)
{
}
/// <summary>
/// Gets the <see cref="MemoryAllocator"/>.
/// </summary>
public MemoryAllocator MemoryAllocator { get; }
/// <summary>
/// Gets the value used for initializing <see cref="ParallelOptions.MaxDegreeOfParallelism"/> when using TPL.
/// </summary>
public int MaxDegreeOfParallelism { get; }
/// <summary>
/// Gets the minimum number of pixels being processed by a single task when parallelizing operations with TPL.
/// Launching tasks for pixel regions below this limit is not worth the overhead.
/// Initialized with <see cref="DefaultMinimumPixelsProcessedPerTask"/> by default,
/// the optimum value is operation specific. (The cheaper the operation, the larger the value is.)
/// </summary>
public int MinimumPixelsProcessedPerTask { get; }
/// <summary>
/// Creates a new instance of <see cref="ParallelExecutionSettings"/>
/// having <see cref="MinimumPixelsProcessedPerTask"/> multiplied by <paramref name="multiplier"/>
/// </summary>
/// <param name="multiplier">The value to multiply <see cref="MinimumPixelsProcessedPerTask"/> with.</param>
/// <returns>The modified <see cref="ParallelExecutionSettings"/>.</returns>
public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier)
{
Guard.MustBeGreaterThan(multiplier, 0, nameof(multiplier));
return new ParallelExecutionSettings(
this.MaxDegreeOfParallelism,
this.MinimumPixelsProcessedPerTask * multiplier,
this.MemoryAllocator);
}
/// <summary>
/// Get the default <see cref="SixLabors.ImageSharp.Advanced.ParallelExecutionSettings"/> for a <see cref="SixLabors.ImageSharp.Configuration"/>
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/>.</param>
/// <returns>The <see cref="ParallelExecutionSettings"/>.</returns>
public static ParallelExecutionSettings FromConfiguration(Configuration configuration)
{
return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator);
}
}
}

198
src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs

@ -0,0 +1,198 @@
// 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.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <content>
/// Utility methods for batched processing of pixel row intervals.
/// Parallel execution is optimized for image processing based on values defined
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </content>
public static partial class ParallelRowIterator
{
private readonly struct RowOperationWrapper<T>
where T : struct, IRowOperation
{
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly T action;
[MethodImpl(InliningOptions.ShortMethod)]
public RowOperationWrapper(
int minY,
int maxY,
int stepY,
in T action)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.minY + (i * this.stepY);
if (yMin >= this.maxY)
{
return;
}
int yMax = Math.Min(yMin + this.stepY, this.maxY);
for (int y = yMin; y < yMax; y++)
{
// Skip the safety copy when invoking a potentially impure method on a readonly field
Unsafe.AsRef(this.action).Invoke(y);
}
}
}
private readonly struct RowOperationWrapper<T, TBuffer>
where T : struct, IRowOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly int width;
private readonly MemoryAllocator allocator;
private readonly T action;
[MethodImpl(InliningOptions.ShortMethod)]
public RowOperationWrapper(
int minY,
int maxY,
int stepY,
int width,
MemoryAllocator allocator,
in T action)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.width = width;
this.allocator = allocator;
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.minY + (i * this.stepY);
if (yMin >= this.maxY)
{
return;
}
int yMax = Math.Min(yMin + this.stepY, this.maxY);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
Span<TBuffer> span = buffer.Memory.Span;
for (int y = yMin; y < yMax; y++)
{
Unsafe.AsRef(this.action).Invoke(y, span);
}
}
}
private readonly struct RowIntervalOperationWrapper<T>
where T : struct, IRowIntervalOperation
{
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(
int minY,
int maxY,
int stepY,
in T operation)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.minY + (i * this.stepY);
if (yMin >= this.maxY)
{
return;
}
int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
// Skip the safety copy when invoking a potentially impure method on a readonly field
Unsafe.AsRef(in this.operation).Invoke(in rows);
}
}
private readonly struct RowIntervalOperationWrapper<T, TBuffer>
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
private readonly int minY;
private readonly int maxY;
private readonly int stepY;
private readonly int width;
private readonly MemoryAllocator allocator;
private readonly T operation;
[MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalOperationWrapper(
int minY,
int maxY,
int stepY,
int width,
MemoryAllocator allocator,
in T operation)
{
this.minY = minY;
this.maxY = maxY;
this.stepY = stepY;
this.width = width;
this.allocator = allocator;
this.operation = operation;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int i)
{
int yMin = this.minY + (i * this.stepY);
if (yMin >= this.maxY)
{
return;
}
int yMax = Math.Min(yMin + this.stepY, this.maxY);
var rows = new RowInterval(yMin, yMax);
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span);
}
}
}
}

288
src/ImageSharp/Advanced/ParallelRowIterator.cs

@ -0,0 +1,288 @@
// 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.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
/// Utility methods for batched processing of pixel row intervals.
/// Parallel execution is optimized for image processing based on values defined
/// <see cref="ParallelExecutionSettings"/> or <see cref="Configuration"/>.
/// Using this class is preferred over direct usage of <see cref="Parallel"/> utility methods.
/// </summary>
public static partial class ParallelRowIterator
{
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IterateRows<T>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, in parallelSettings, in operation);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
public static void IterateRows<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowOperation
{
ValidateRectangle(rectangle);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
for (int y = top; y < bottom; y++)
{
Unsafe.AsRef(operation).Invoke(y);
}
return;
}
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowOperationWrapper<T>(top, bottom, verticalStep, in operation);
Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
public static void IterateRows<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowOperation<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows<T, TBuffer>(rectangle, in parallelSettings, in operation);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches.
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single row.</param>
public static void IterateRows<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowOperation<TBuffer>
where TBuffer : unmanaged
{
ValidateRectangle(rectangle);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width);
Span<TBuffer> span = buffer.Memory.Span;
for (int y = top; y < bottom; y++)
{
Unsafe.AsRef(operation).Invoke(y, span);
}
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, in operation);
Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void IterateRowIntervals<T>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRowIntervals(rectangle, in parallelSettings, in operation);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRowIntervals<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowIntervalOperation
{
ValidateRectangle(rectangle);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
var rows = new RowInterval(top, bottom);
Unsafe.AsRef(in operation).Invoke(in rows);
return;
}
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowIntervalOperationWrapper<T>(top, bottom, verticalStep, in operation);
Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRowIntervals<T, TBuffer>(Configuration configuration, Rectangle rectangle, in T operation)
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRowIntervals<T, TBuffer>(rectangle, in parallelSettings, in operation);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s
/// instantiating a temporary buffer for each <paramref name="operation"/> invocation.
/// </summary>
/// <typeparam name="T">The type of row operation to perform.</typeparam>
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="operation">The operation defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRowIntervals<T, TBuffer>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
in T operation)
where T : struct, IRowIntervalOperation<TBuffer>
where TBuffer : unmanaged
{
ValidateRectangle(rectangle);
int top = rectangle.Top;
int bottom = rectangle.Bottom;
int width = rectangle.Width;
int height = rectangle.Height;
int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
MemoryAllocator allocator = parallelSettings.MemoryAllocator;
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
var rows = new RowInterval(top, bottom);
using IMemoryOwner<TBuffer> buffer = allocator.Allocate<TBuffer>(width);
Unsafe.AsRef(operation).Invoke(in rows, buffer.Memory.Span);
return;
}
int verticalStep = DivideCeil(height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
var wrappingOperation = new RowIntervalOperationWrapper<T, TBuffer>(top, bottom, verticalStep, width, allocator, in operation);
Parallel.For(
0,
numOfSteps,
parallelOptions,
wrappingOperation.Invoke);
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor);
private static void ValidateRectangle(Rectangle rectangle)
{
Guard.MustBeGreaterThan(
rectangle.Width,
0,
$"{nameof(rectangle)}.{nameof(rectangle.Width)}");
Guard.MustBeGreaterThan(
rectangle.Height,
0,
$"{nameof(rectangle)}.{nameof(rectangle.Height)}");
}
}
}

211
src/ImageSharp/Color/Color.NamedColors.cs

@ -1,13 +1,19 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp
{
/// <content>
/// Contains static named color values.
/// <see href="https://www.w3.org/TR/css-color-3/"/>
/// </content>
public readonly partial struct Color
{
private static readonly Lazy<Dictionary<string, Color>> NamedColorsLookupLazy = new Lazy<Dictionary<string, Color>>(CreateNamedColorsLookup, true);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0F8FF.
/// </summary>
@ -111,7 +117,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00FFFF.
/// </summary>
public static readonly Color Cyan = FromRgba(0, 255, 255, 255);
public static readonly Color Cyan = Aqua;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00008B.
@ -138,6 +144,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color DarkGreen = FromRgba(0, 100, 0, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #A9A9A9.
/// </summary>
public static readonly Color DarkGrey = DarkGray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #BDB76B.
/// </summary>
@ -174,9 +185,9 @@ namespace SixLabors.ImageSharp
public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8FBC8B.
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #8FBC8F.
/// </summary>
public static readonly Color DarkSeaGreen = FromRgba(143, 188, 139, 255);
public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #483D8B.
@ -188,6 +199,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color DarkSlateGray = FromRgba(47, 79, 79, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #2F4F4F.
/// </summary>
public static readonly Color DarkSlateGrey = DarkSlateGray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00CED1.
/// </summary>
@ -213,6 +229,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color DimGray = FromRgba(105, 105, 105, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #696969.
/// </summary>
public static readonly Color DimGrey = DimGray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #1E90FF.
/// </summary>
@ -273,6 +294,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color GreenYellow = FromRgba(173, 255, 47, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #808080.
/// </summary>
public static readonly Color Grey = Gray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #F0FFF0.
/// </summary>
@ -353,6 +379,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color LightGreen = FromRgba(144, 238, 144, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #D3D3D3.
/// </summary>
public static readonly Color LightGrey = LightGray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFB6C1.
/// </summary>
@ -378,6 +409,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color LightSlateGray = FromRgba(119, 136, 153, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #778899.
/// </summary>
public static readonly Color LightSlateGrey = LightSlateGray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #B0C4DE.
/// </summary>
@ -406,7 +442,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FF00FF.
/// </summary>
public static readonly Color Magenta = FromRgba(255, 0, 255, 255);
public static readonly Color Magenta = Fuchsia;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #800000.
@ -643,6 +679,11 @@ namespace SixLabors.ImageSharp
/// </summary>
public static readonly Color SlateGray = FromRgba(112, 128, 144, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #708090.
/// </summary>
public static readonly Color SlateGrey = SlateGray;
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFAFA.
/// </summary>
@ -679,9 +720,9 @@ namespace SixLabors.ImageSharp
public static readonly Color Tomato = FromRgba(255, 99, 71, 255);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFFF.
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00000000.
/// </summary>
public static readonly Color Transparent = FromRgba(255, 255, 255, 0);
public static readonly Color Transparent = FromRgba(0, 0, 0, 0);
/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #40E0D0.
@ -717,5 +758,161 @@ namespace SixLabors.ImageSharp
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #9ACD32.
/// </summary>
public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255);
private static Dictionary<string, Color> CreateNamedColorsLookup()
{
return new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase)
{
{ nameof(AliceBlue), AliceBlue },
{ nameof(AntiqueWhite), AntiqueWhite },
{ nameof(Aqua), Aqua },
{ nameof(Aquamarine), Aquamarine },
{ nameof(Azure), Azure },
{ nameof(Beige), Beige },
{ nameof(Bisque), Bisque },
{ nameof(Black), Black },
{ nameof(BlanchedAlmond), BlanchedAlmond },
{ nameof(Blue), Blue },
{ nameof(BlueViolet), BlueViolet },
{ nameof(Brown), Brown },
{ nameof(BurlyWood), BurlyWood },
{ nameof(CadetBlue), CadetBlue },
{ nameof(Chartreuse), Chartreuse },
{ nameof(Chocolate), Chocolate },
{ nameof(Coral), Coral },
{ nameof(CornflowerBlue), CornflowerBlue },
{ nameof(Cornsilk), Cornsilk },
{ nameof(Crimson), Crimson },
{ nameof(Cyan), Cyan },
{ nameof(DarkBlue), DarkBlue },
{ nameof(DarkCyan), DarkCyan },
{ nameof(DarkGoldenrod), DarkGoldenrod },
{ nameof(DarkGray), DarkGray },
{ nameof(DarkGreen), DarkGreen },
{ nameof(DarkGrey), DarkGrey },
{ nameof(DarkKhaki), DarkKhaki },
{ nameof(DarkMagenta), DarkMagenta },
{ nameof(DarkOliveGreen), DarkOliveGreen },
{ nameof(DarkOrange), DarkOrange },
{ nameof(DarkOrchid), DarkOrchid },
{ nameof(DarkRed), DarkRed },
{ nameof(DarkSalmon), DarkSalmon },
{ nameof(DarkSeaGreen), DarkSeaGreen },
{ nameof(DarkSlateBlue), DarkSlateBlue },
{ nameof(DarkSlateGray), DarkSlateGray },
{ nameof(DarkSlateGrey), DarkSlateGrey },
{ nameof(DarkTurquoise), DarkTurquoise },
{ nameof(DarkViolet), DarkViolet },
{ nameof(DeepPink), DeepPink },
{ nameof(DeepSkyBlue), DeepSkyBlue },
{ nameof(DimGray), DimGray },
{ nameof(DimGrey), DimGrey },
{ nameof(DodgerBlue), DodgerBlue },
{ nameof(Firebrick), Firebrick },
{ nameof(FloralWhite), FloralWhite },
{ nameof(ForestGreen), ForestGreen },
{ nameof(Fuchsia), Fuchsia },
{ nameof(Gainsboro), Gainsboro },
{ nameof(GhostWhite), GhostWhite },
{ nameof(Gold), Gold },
{ nameof(Goldenrod), Goldenrod },
{ nameof(Gray), Gray },
{ nameof(Green), Green },
{ nameof(GreenYellow), GreenYellow },
{ nameof(Grey), Grey },
{ nameof(Honeydew), Honeydew },
{ nameof(HotPink), HotPink },
{ nameof(IndianRed), IndianRed },
{ nameof(Indigo), Indigo },
{ nameof(Ivory), Ivory },
{ nameof(Khaki), Khaki },
{ nameof(Lavender), Lavender },
{ nameof(LavenderBlush), LavenderBlush },
{ nameof(LawnGreen), LawnGreen },
{ nameof(LemonChiffon), LemonChiffon },
{ nameof(LightBlue), LightBlue },
{ nameof(LightCoral), LightCoral },
{ nameof(LightCyan), LightCyan },
{ nameof(LightGoldenrodYellow), LightGoldenrodYellow },
{ nameof(LightGray), LightGray },
{ nameof(LightGreen), LightGreen },
{ nameof(LightGrey), LightGrey },
{ nameof(LightPink), LightPink },
{ nameof(LightSalmon), LightSalmon },
{ nameof(LightSeaGreen), LightSeaGreen },
{ nameof(LightSkyBlue), LightSkyBlue },
{ nameof(LightSlateGray), LightSlateGray },
{ nameof(LightSlateGrey), LightSlateGrey },
{ nameof(LightSteelBlue), LightSteelBlue },
{ nameof(LightYellow), LightYellow },
{ nameof(Lime), Lime },
{ nameof(LimeGreen), LimeGreen },
{ nameof(Linen), Linen },
{ nameof(Magenta), Magenta },
{ nameof(Maroon), Maroon },
{ nameof(MediumAquamarine), MediumAquamarine },
{ nameof(MediumBlue), MediumBlue },
{ nameof(MediumOrchid), MediumOrchid },
{ nameof(MediumPurple), MediumPurple },
{ nameof(MediumSeaGreen), MediumSeaGreen },
{ nameof(MediumSlateBlue), MediumSlateBlue },
{ nameof(MediumSpringGreen), MediumSpringGreen },
{ nameof(MediumTurquoise), MediumTurquoise },
{ nameof(MediumVioletRed), MediumVioletRed },
{ nameof(MidnightBlue), MidnightBlue },
{ nameof(MintCream), MintCream },
{ nameof(MistyRose), MistyRose },
{ nameof(Moccasin), Moccasin },
{ nameof(NavajoWhite), NavajoWhite },
{ nameof(Navy), Navy },
{ nameof(OldLace), OldLace },
{ nameof(Olive), Olive },
{ nameof(OliveDrab), OliveDrab },
{ nameof(Orange), Orange },
{ nameof(OrangeRed), OrangeRed },
{ nameof(Orchid), Orchid },
{ nameof(PaleGoldenrod), PaleGoldenrod },
{ nameof(PaleGreen), PaleGreen },
{ nameof(PaleTurquoise), PaleTurquoise },
{ nameof(PaleVioletRed), PaleVioletRed },
{ nameof(PapayaWhip), PapayaWhip },
{ nameof(PeachPuff), PeachPuff },
{ nameof(Peru), Peru },
{ nameof(Pink), Pink },
{ nameof(Plum), Plum },
{ nameof(PowderBlue), PowderBlue },
{ nameof(Purple), Purple },
{ nameof(RebeccaPurple), RebeccaPurple },
{ nameof(Red), Red },
{ nameof(RosyBrown), RosyBrown },
{ nameof(RoyalBlue), RoyalBlue },
{ nameof(SaddleBrown), SaddleBrown },
{ nameof(Salmon), Salmon },
{ nameof(SandyBrown), SandyBrown },
{ nameof(SeaGreen), SeaGreen },
{ nameof(SeaShell), SeaShell },
{ nameof(Sienna), Sienna },
{ nameof(Silver), Silver },
{ nameof(SkyBlue), SkyBlue },
{ nameof(SlateBlue), SlateBlue },
{ nameof(SlateGray), SlateGray },
{ nameof(SlateGrey), SlateGrey },
{ nameof(Snow), Snow },
{ nameof(SpringGreen), SpringGreen },
{ nameof(SteelBlue), SteelBlue },
{ nameof(Tan), Tan },
{ nameof(Teal), Teal },
{ nameof(Thistle), Thistle },
{ nameof(Tomato), Tomato },
{ nameof(Transparent), Transparent },
{ nameof(Turquoise), Turquoise },
{ nameof(Violet), Violet },
{ nameof(Wheat), Wheat },
{ nameof(White), White },
{ nameof(WhiteSmoke), WhiteSmoke },
{ nameof(Yellow), Yellow },
{ nameof(YellowGreen), YellowGreen }
};
}
}
}
}

222
src/ImageSharp/Color/Color.WernerPalette.cs

@ -20,116 +20,116 @@ namespace SixLabors.ImageSharp
private static Color[] CreateWernerPalette() => new[]
{
FromHex("#f1e9cd"),
FromHex("#f2e7cf"),
FromHex("#ece6d0"),
FromHex("#f2eacc"),
FromHex("#f3e9ca"),
FromHex("#f2ebcd"),
FromHex("#e6e1c9"),
FromHex("#e2ddc6"),
FromHex("#cbc8b7"),
FromHex("#bfbbb0"),
FromHex("#bebeb3"),
FromHex("#b7b5ac"),
FromHex("#bab191"),
FromHex("#9c9d9a"),
FromHex("#8a8d84"),
FromHex("#5b5c61"),
FromHex("#555152"),
FromHex("#413f44"),
FromHex("#454445"),
FromHex("#423937"),
FromHex("#433635"),
FromHex("#252024"),
FromHex("#241f20"),
FromHex("#281f3f"),
FromHex("#1c1949"),
FromHex("#4f638d"),
FromHex("#383867"),
FromHex("#5c6b8f"),
FromHex("#657abb"),
FromHex("#6f88af"),
FromHex("#7994b5"),
FromHex("#6fb5a8"),
FromHex("#719ba2"),
FromHex("#8aa1a6"),
FromHex("#d0d5d3"),
FromHex("#8590ae"),
FromHex("#3a2f52"),
FromHex("#39334a"),
FromHex("#6c6d94"),
FromHex("#584c77"),
FromHex("#533552"),
FromHex("#463759"),
FromHex("#bfbac0"),
FromHex("#77747f"),
FromHex("#4a475c"),
FromHex("#b8bfaf"),
FromHex("#b2b599"),
FromHex("#979c84"),
FromHex("#5d6161"),
FromHex("#61ac86"),
FromHex("#a4b6a7"),
FromHex("#adba98"),
FromHex("#93b778"),
FromHex("#7d8c55"),
FromHex("#33431e"),
FromHex("#7c8635"),
FromHex("#8e9849"),
FromHex("#c2c190"),
FromHex("#67765b"),
FromHex("#ab924b"),
FromHex("#c8c76f"),
FromHex("#ccc050"),
FromHex("#ebdd99"),
FromHex("#ab9649"),
FromHex("#dbc364"),
FromHex("#e6d058"),
FromHex("#ead665"),
FromHex("#d09b2c"),
FromHex("#a36629"),
FromHex("#a77d35"),
FromHex("#f0d696"),
FromHex("#d7c485"),
FromHex("#f1d28c"),
FromHex("#efcc83"),
FromHex("#f3daa7"),
FromHex("#dfa837"),
FromHex("#ebbc71"),
FromHex("#d17c3f"),
FromHex("#92462f"),
FromHex("#be7249"),
FromHex("#bb603c"),
FromHex("#c76b4a"),
FromHex("#a75536"),
FromHex("#b63e36"),
FromHex("#b5493a"),
FromHex("#cd6d57"),
FromHex("#711518"),
FromHex("#e9c49d"),
FromHex("#eedac3"),
FromHex("#eecfbf"),
FromHex("#ce536b"),
FromHex("#b74a70"),
FromHex("#b7757c"),
FromHex("#612741"),
FromHex("#7a4848"),
FromHex("#3f3033"),
FromHex("#8d746f"),
FromHex("#4d3635"),
FromHex("#6e3b31"),
FromHex("#864735"),
FromHex("#553d3a"),
FromHex("#613936"),
FromHex("#7a4b3a"),
FromHex("#946943"),
FromHex("#c39e6d"),
FromHex("#513e32"),
FromHex("#8b7859"),
FromHex("#9b856b"),
FromHex("#766051"),
FromHex("#453b32")
ParseHex("#f1e9cd"),
ParseHex("#f2e7cf"),
ParseHex("#ece6d0"),
ParseHex("#f2eacc"),
ParseHex("#f3e9ca"),
ParseHex("#f2ebcd"),
ParseHex("#e6e1c9"),
ParseHex("#e2ddc6"),
ParseHex("#cbc8b7"),
ParseHex("#bfbbb0"),
ParseHex("#bebeb3"),
ParseHex("#b7b5ac"),
ParseHex("#bab191"),
ParseHex("#9c9d9a"),
ParseHex("#8a8d84"),
ParseHex("#5b5c61"),
ParseHex("#555152"),
ParseHex("#413f44"),
ParseHex("#454445"),
ParseHex("#423937"),
ParseHex("#433635"),
ParseHex("#252024"),
ParseHex("#241f20"),
ParseHex("#281f3f"),
ParseHex("#1c1949"),
ParseHex("#4f638d"),
ParseHex("#383867"),
ParseHex("#5c6b8f"),
ParseHex("#657abb"),
ParseHex("#6f88af"),
ParseHex("#7994b5"),
ParseHex("#6fb5a8"),
ParseHex("#719ba2"),
ParseHex("#8aa1a6"),
ParseHex("#d0d5d3"),
ParseHex("#8590ae"),
ParseHex("#3a2f52"),
ParseHex("#39334a"),
ParseHex("#6c6d94"),
ParseHex("#584c77"),
ParseHex("#533552"),
ParseHex("#463759"),
ParseHex("#bfbac0"),
ParseHex("#77747f"),
ParseHex("#4a475c"),
ParseHex("#b8bfaf"),
ParseHex("#b2b599"),
ParseHex("#979c84"),
ParseHex("#5d6161"),
ParseHex("#61ac86"),
ParseHex("#a4b6a7"),
ParseHex("#adba98"),
ParseHex("#93b778"),
ParseHex("#7d8c55"),
ParseHex("#33431e"),
ParseHex("#7c8635"),
ParseHex("#8e9849"),
ParseHex("#c2c190"),
ParseHex("#67765b"),
ParseHex("#ab924b"),
ParseHex("#c8c76f"),
ParseHex("#ccc050"),
ParseHex("#ebdd99"),
ParseHex("#ab9649"),
ParseHex("#dbc364"),
ParseHex("#e6d058"),
ParseHex("#ead665"),
ParseHex("#d09b2c"),
ParseHex("#a36629"),
ParseHex("#a77d35"),
ParseHex("#f0d696"),
ParseHex("#d7c485"),
ParseHex("#f1d28c"),
ParseHex("#efcc83"),
ParseHex("#f3daa7"),
ParseHex("#dfa837"),
ParseHex("#ebbc71"),
ParseHex("#d17c3f"),
ParseHex("#92462f"),
ParseHex("#be7249"),
ParseHex("#bb603c"),
ParseHex("#c76b4a"),
ParseHex("#a75536"),
ParseHex("#b63e36"),
ParseHex("#b5493a"),
ParseHex("#cd6d57"),
ParseHex("#711518"),
ParseHex("#e9c49d"),
ParseHex("#eedac3"),
ParseHex("#eecfbf"),
ParseHex("#ce536b"),
ParseHex("#b74a70"),
ParseHex("#b7757c"),
ParseHex("#612741"),
ParseHex("#7a4848"),
ParseHex("#3f3033"),
ParseHex("#8d746f"),
ParseHex("#4d3635"),
ParseHex("#6e3b31"),
ParseHex("#864735"),
ParseHex("#553d3a"),
ParseHex("#613936"),
ParseHex("#7a4b3a"),
ParseHex("#946943"),
ParseHex("#c39e6d"),
ParseHex("#513e32"),
ParseHex("#8b7859"),
ParseHex("#9b856b"),
ParseHex("#766051"),
ParseHex("#453b32")
};
}
}
}

130
src/ImageSharp/Color/Color.cs

@ -95,21 +95,102 @@ namespace SixLabors.ImageSharp
public static Color FromRgb(byte r, byte g, byte b) => new Color(r, g, b);
/// <summary>
/// Creates a new <see cref="Color"/> instance from the string representing a color in hexadecimal form.
/// Creates a new instance of the <see cref="Color"/> struct
/// from the given hexadecimal string.
/// </summary>
/// <param name="hex">
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <returns>Returns a <see cref="Color"/> that represents the color defined by the provided RGBA hex string.</returns>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static Color FromHex(string hex)
public static Color ParseHex(string hex)
{
Rgba32 rgba = Rgba32.FromHex(hex);
var rgba = Rgba32.ParseHex(hex);
return new Color(rgba);
}
/// <summary>
/// Attempts to creates a new instance of the <see cref="Color"/> struct
/// from the given hexadecimal string.
/// </summary>
/// <param name="hex">
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <param name="result">When this method returns, contains the <see cref="Color"/> equivalent of the hexadecimal input.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool TryParseHex(string hex, out Color result)
{
result = default;
if (Rgba32.TryParseHex(hex, out Rgba32 rgba))
{
result = new Color(rgba);
return true;
}
return false;
}
/// <summary>
/// Creates a new instance of the <see cref="Color"/> struct
/// from the given input string.
/// </summary>
/// <param name="input">
/// The name of the color or the hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Parse(string input)
{
Guard.NotNull(input, nameof(input));
if (!TryParse(input, out Color color))
{
throw new ArgumentException("Input string is not in the correct format.", nameof(input));
}
return color;
}
/// <summary>
/// Attempts to creates a new instance of the <see cref="Color"/> struct
/// from the given input string.
/// </summary>
/// <param name="input">
/// The name of the color or the hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <param name="result">When this method returns, contains the <see cref="Color"/> equivalent of the hexadecimal input.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public static bool TryParse(string input, out Color result)
{
result = default;
if (string.IsNullOrWhiteSpace(input))
{
return false;
}
if (NamedColorsLookupLazy.Value.TryGetValue(input, out result))
{
return true;
}
return TryParseHex(input, out result);
}
/// <summary>
/// Alters the alpha channel of the color, returning a new instance.
/// </summary>
@ -117,7 +198,7 @@ namespace SixLabors.ImageSharp
/// <returns>The color having it's alpha channel altered.</returns>
public Color WithAlpha(float alpha)
{
Vector4 v = (Vector4)this;
var v = (Vector4)this;
v.W = alpha;
return new Color(v);
}
@ -133,20 +214,37 @@ namespace SixLabors.ImageSharp
public override string ToString() => this.ToHex();
/// <summary>
/// Converts the color instance to an <see cref="IPixel{TSelf}"/>
/// implementation defined by <typeparamref name="TPixel"/>.
/// Converts the color instance to a specified <see cref="IPixel{TSelf}"/> type.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <returns>The pixel value.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public TPixel ToPixel<TPixel>()
where TPixel : struct, IPixel<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
TPixel pixel = default;
pixel.FromRgba64(this.data);
return pixel;
}
/// <summary>
/// Bulk converts a span of <see cref="Color"/> to a span of a specified <see cref="IPixel{TSelf}"/> type.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void ToPixel<TPixel>(
Configuration configuration,
ReadOnlySpan<Color> source,
Span<TPixel> destination)
where TPixel : unmanaged, IPixel<TPixel>
{
ReadOnlySpan<Rgba64> rgba64Span = MemoryMarshal.Cast<Color, Rgba64>(source);
PixelOperations<TPixel>.Instance.FromRgba64(configuration, rgba64Span, destination);
}
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Color other)
@ -166,19 +264,5 @@ namespace SixLabors.ImageSharp
{
return this.data.PackedValue.GetHashCode();
}
/// <summary>
/// Bulk convert a span of <see cref="Color"/> to a span of a specified pixel type.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void ToPixel<TPixel>(
Configuration configuration,
ReadOnlySpan<Color> source,
Span<TPixel> destination)
where TPixel : struct, IPixel<TPixel>
{
ReadOnlySpan<Rgba64> rgba64Span = MemoryMarshal.Cast<Color, Rgba64>(source);
PixelOperations<TPixel>.Instance.FromRgba64(Configuration.Default, rgba64Span, destination);
}
}
}
}

79
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -1,79 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
// ReSharper disable CompareOfFloatsByEqualityOperator
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents the coordinates of CIEXY chromaticity space.
/// </summary>
public readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>
{
/// <summary>
/// Gets the chromaticity X-coordinate.
/// </summary>
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public readonly float X;
/// <summary>
/// Gets the chromaticity Y-coordinate
/// </summary>
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public readonly float Y;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyChromaticityCoordinates(float x, float y)
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => left.Equals(right);
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
/// </summary>
/// <param name="left">The <see cref="CieXyChromaticityCoordinates"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="CieXyChromaticityCoordinates"/> on the right side of the operand.</param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => !left.Equals(right);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj) => obj is CieXyChromaticityCoordinates other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
}
}

2
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk(Vector4 vector)
{
vector = Vector4.Clamp(vector, Min, Max);
vector = Vector4Utilities.FastClamp(vector, Min, Max);
this.C = vector.X;
this.M = vector.Y;
this.Y = vector.Z;

4
src/ImageSharp/ColorSpaces/Companding/LCompanding.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;
@ -35,4 +35,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
public static float Compress(float channel)
=> channel <= CieConstants.Epsilon ? (channel * CieConstants.Kappa) / 100F : (1.16F * MathF.Pow(channel, 0.3333333F)) - 0.16F;
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save