Browse Source

Merge branch 'master' into png-check-data-chunk

af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
874164b67e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .gitignore
  2. 4
      .travis.yml
  3. 2
      .vscode/launch.json
  4. 4
      .vscode/tasks.json
  5. 32
      ImageSharp.sln
  6. 14
      ImageSharp.v2.ncrunchsolution
  7. 6
      ImageSharp.v3.ncrunchsolution
  8. 1
      NuGet.config
  9. 4
      config.wyam
  10. 153
      packages.xml
  11. 12
      samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj
  12. 111
      samples/AvatarWithRoundedCorner/Program.cs
  13. 3
      samples/AvatarWithRoundedCorner/fb.jpg
  14. 12
      samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj
  15. 23
      samples/ChangeDefaultEncoderOptions/Program.cs
  16. 13
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  17. 15
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  18. 15
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  19. 15
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  20. 34
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  21. 53
      src/ImageSharp.Drawing/DrawImage.cs
  22. 6
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  23. 2
      src/ImageSharp.Drawing/Paths/ShapePath.cs
  24. 17
      src/ImageSharp.Drawing/Paths/ShapeRegion.cs
  25. 86
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  26. 24
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  27. 129
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  28. 11
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  29. 1
      src/ImageSharp/ApplyProcessors.cs
  30. 2
      src/ImageSharp/ColorSpaces/CieLab.cs
  31. 2
      src/ImageSharp/ColorSpaces/CieLch.cs
  32. 2
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  33. 2
      src/ImageSharp/ColorSpaces/CieLuv.cs
  34. 6
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  35. 2
      src/ImageSharp/ColorSpaces/CieXyy.cs
  36. 2
      src/ImageSharp/ColorSpaces/CieXyz.cs
  37. 2
      src/ImageSharp/ColorSpaces/Cmyk.cs
  38. 3
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  39. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs
  40. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs
  41. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
  42. 2
      src/ImageSharp/ColorSpaces/Hsl.cs
  43. 2
      src/ImageSharp/ColorSpaces/Hsv.cs
  44. 2
      src/ImageSharp/ColorSpaces/HunterLab.cs
  45. 2
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  46. 2
      src/ImageSharp/ColorSpaces/Lms.cs
  47. 2
      src/ImageSharp/ColorSpaces/Rgb.cs
  48. 2
      src/ImageSharp/ColorSpaces/YCbCr.cs
  49. 14
      src/ImageSharp/Common/Extensions/ComparableExtensions.cs
  50. 12
      src/ImageSharp/Common/Extensions/SimdUtils.cs
  51. 21
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  52. 28
      src/ImageSharp/Common/Extensions/Vector4Extensions.cs
  53. 21
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  54. 60
      src/ImageSharp/Common/Helpers/ParallelFor.cs
  55. 406
      src/ImageSharp/Configuration.cs
  56. 19
      src/ImageSharp/DefaultInternalImageProcessorContext.cs
  57. 24
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
  58. 24
      src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
  59. 56
      src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs
  60. 34
      src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs
  61. 36
      src/ImageSharp/Dithering/Ordered/BayerDither.cs
  62. 19
      src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
  63. 19
      src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
  64. 19
      src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
  65. 7
      src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
  66. 31
      src/ImageSharp/Dithering/Ordered/KnownDitherers.cs
  67. 50
      src/ImageSharp/Dithering/Ordered/OrderedDither.cs
  68. 19
      src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs
  69. 53
      src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
  70. 94
      src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs
  71. 58
      src/ImageSharp/Dithering/error_diffusion.txt
  72. 6
      src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
  73. 37
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  74. 3
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  75. 28
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  76. 2
      src/ImageSharp/Formats/Bmp/ImageExtensions.cs
  77. 6
      src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
  78. 64
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  79. 4
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  80. 26
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  81. 2
      src/ImageSharp/Formats/Gif/ImageExtensions.cs
  82. 53
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  83. 58
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  84. 2
      src/ImageSharp/Formats/Gif/PackedField.cs
  85. 186
      src/ImageSharp/Formats/ImageFormatManager.cs
  86. 5
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  87. 11
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs
  88. 4
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  89. 2
      src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
  90. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  91. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  92. 4
      src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs
  93. 2
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
  94. 2
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs
  95. 8
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs
  96. 15
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
  97. 26
      src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs
  98. 43
      src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt
  99. 129
      src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs
  100. 18
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs

3
.gitignore

@ -26,6 +26,9 @@ bld/
# Visual Studo 2015 cache/options directory
.vs/
# Jetbrains Rider cache/options directory
.idea/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

4
.travis.yml

@ -6,7 +6,7 @@ matrix:
- os: linux # Ubuntu 14.04
dist: trusty
sudo: required
dotnet: 1.0.4
dotnet: 2.1.4
mono: latest
# - os: osx # OSX 10.11
# osx_image: xcode7.3.1
@ -21,7 +21,7 @@ branches:
script:
- git submodule -q update --init
- dotnet restore
- dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1"
- dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp2.0"
env:
global:

2
.vscode/launch.json

@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceRoot}/samples/AvatarWithRoundedCorner/bin/Debug/netcoreapp1.1/AvatarWithRoundedCorner.dll",
"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

4
.vscode/tasks.json

@ -16,13 +16,13 @@
{
"taskName": "build benchmark",
"suppressTaskName": true,
"args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp1.1", "-c", "Release" ],
"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", "netcoreapp1.1"],
"args": ["tests/ImageSharp.Tests/ImageSharp.Tests.csproj", "-c", "release", "-f", "netcoreapp2.0"],
"isTestCommand": true,
"showOutput": "always",
"problemMatcher": "$msCompile"

32
ImageSharp.sln

@ -43,12 +43,6 @@ 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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}"
EndProject
Global
@ -112,30 +106,6 @@ Global
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.Build.0 = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.ActiveCfg = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.Build.0 = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.ActiveCfg = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.Build.0 = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.Build.0 = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU
{561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -158,8 +128,6 @@ Global
{2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2}
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2}
{561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

14
ImageSharp.v2.ncrunchsolution

@ -1,14 +0,0 @@
<SolutionConfiguration>
<FileVersion>1</FileVersion>
<InferProjectReferencesUsingAssemblyNames>false</InferProjectReferencesUsingAssemblyNames>
<AllowParallelTestExecution>false</AllowParallelTestExecution>
<AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>
<FrameworkUtilisationTypeForGallio>UseStaticAnalysis</FrameworkUtilisationTypeForGallio>
<FrameworkUtilisationTypeForMSpec>UseStaticAnalysis</FrameworkUtilisationTypeForMSpec>
<FrameworkUtilisationTypeForMSTest>UseStaticAnalysis</FrameworkUtilisationTypeForMSTest>
<FrameworkUtilisationTypeForXUnit2>UseDynamicAnalysis</FrameworkUtilisationTypeForXUnit2>
<NCrunchCacheStoragePath />
<MetricsExclusionList>
</MetricsExclusionList>
</SolutionConfiguration>

6
ImageSharp.v3.ncrunchsolution

@ -1,6 +0,0 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>

1
NuGet.config

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="myget.org sixlabors" value="https://www.myget.org/F/sixlabors/api/v3/index.json" />
<add key="myget.org dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />

4
config.wyam

@ -1,4 +0,0 @@
#recipe Docs
Settings[Keys.Host] = "imagesharp.org";
Settings[Keys.Title] = "Image Sharp";
FileSystem.OutputPath = "./docs";

153
packages.xml

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Wyam.Docs" version="0.18.6" targetFramework="net462">
<package id="AngleSharp" version="0.9.9" targetFramework="net462" />
<package id="CommonServiceLocator" version="1.3" targetFramework="net462" />
<package id="DotlessClientOnly" version="1.5.2" targetFramework="net462" />
<package id="ManagedEsent" version="1.9.4" targetFramework="net462" />
<package id="Markdig" version="0.10.6" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.Elfie" version="0.10.6" targetFramework="net462" />
<package id="Microsoft.Composition" version="1.0.30" targetFramework="net462" />
<package id="Microsoft.CSharp" version="4.3.0" targetFramework="net462" />
<package id="Microsoft.DotNet.InternalAbstractions" version="1.0.0" targetFramework="net462" />
<package id="Microsoft.DotNet.PlatformAbstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net462" />
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyModel" version="1.1.1" targetFramework="net462" />
<package id="SharpScss" version="1.3.4" targetFramework="net462" />
<package id="System.AppContext" version="4.3.0" targetFramework="net462" />
<package id="System.Buffers" version="4.3.0" targetFramework="net462" />
<package id="System.Collections" version="4.3.0" targetFramework="net462" />
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net462" />
<package id="System.Collections.Immutable" version="1.3.1" targetFramework="net462" />
<package id="System.ComponentModel" version="4.3.0" targetFramework="net462" />
<package id="System.Console" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.Contracts" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net462" />
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net462" />
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net462" />
<package id="System.Globalization" version="4.3.0" targetFramework="net462" />
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net462" />
<package id="System.IO" version="4.3.0" targetFramework="net462" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net462" />
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net462" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net462" />
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net462" />
<package id="System.Linq" version="4.3.0" targetFramework="net462" />
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net462" />
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net462" />
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net462" />
<package id="System.ObjectModel" version="4.3.0" targetFramework="net462" />
<package id="System.Reflection" version="4.3.0" targetFramework="net462" />
<package id="ReflectionMagic" version="3.0.0" targetFramework="net462" />
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net462" />
<package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net462" />
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net462" />
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net462" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net462" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net462" />
<package id="System.Net.Http" version="4.3.1" targetFramework="net462" />
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net462" />
<package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net462" />
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net462" />
<package id="System.Text.Encodings.Web" version="4.3.0" targetFramework="net462" />
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net462" />
<package id="System.Threading" version="4.3.0" targetFramework="net462" />
<package id="ConcurrentHashSet" version="0.3.1" targetFramework="net462" />
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net462" />
<package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net462" />
<package id="System.Threading.Thread" version="4.3.0" targetFramework="net462" />
<package id="Microsoft.Build.Framework" version="15.1.1012" targetFramework="net462" />
<package id="Microsoft.Build" version="15.1.1012" targetFramework="net462" />
<package id="Microsoft.Build.Utilities.Core" version="15.1.1012" targetFramework="net462" />
<package id="Microsoft.Build.Tasks.Core" version="15.1.1012" targetFramework="net462" />
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net462" />
<package id="System.ValueTuple" version="4.3.0" targetFramework="net462" />
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net462" />
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net462" />
<package id="NETStandard.Library" version="1.6.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Cryptography.Internal" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.DataProtection.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Diagnostics.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Html.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.JsonPatch" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Razor" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Razor.Runtime" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.DependencyInjection" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.FileSystemGlobbing" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Localization.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.ObjectPool" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.PlatformAbstractions" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Primitives" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Http.Features" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Http.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.ResponseCaching.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Routing.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.Caching.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.Configuration.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Hosting.Server.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.FileProviders.Abstractions" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Hosting.Abstractions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.FileProviders.Composite" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.FileProviders.Physical" version="1.1.0" targetFramework="net462" />
<package id="Microsoft.Extensions.Options" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Authorization" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.DataProtection" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.Caching.Memory" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.Razor.Host" version="1.1.2" targetFramework="net462" />
<package id="Microsoft.Extensions.Localization" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Extensions.WebEncoders" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.Net.Http.Headers" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Http.Extensions" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.Abstractions" version="1.1.2" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Routing" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.WebUtilities" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Antiforgery" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Http" version="1.1.1" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.Core" version="1.1.2" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.DataAnnotations" version="1.1.2" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.Formatters.Json" version="1.1.2" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.ViewFeatures" version="1.1.2" targetFramework="net462" />
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net462" />
<package id="System.Xml.XPath" version="4.3.0" targetFramework="net462" />
<package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.Common" version="2.0.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.CSharp" version="2.0.0" targetFramework="net462" />
<package id="Microsoft.AspNetCore.Mvc.Razor" version="1.1.2" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.VisualBasic" version="2.0.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.Workspaces.Common" version="2.0.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.0.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="2.0.0" targetFramework="net462" />
<package id="Microsoft.CodeAnalysis" version="2.0.0" targetFramework="net462" />
<package id="Wyam.CodeAnalysis" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Feeds" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Html" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Less" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Markdown" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Razor" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Sass" version="0.18.6" targetFramework="net462" />
<package id="Wyam.SearchIndex" version="0.18.6" targetFramework="net462" />
<package id="YamlDotNet" version="3.9.0" targetFramework="net462" />
<package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net462" />
<package id="Wyam.Yaml" version="0.18.6" targetFramework="net462" />
<package id="Wyam.Web" version="0.18.6" targetFramework="net462" />
</package>
<package id="Wyam.Docs.Samson" version="0.18.6" targetFramework="net462" />
</packages>

12
samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj

@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />
</ItemGroup>
</Project>

111
samples/AvatarWithRoundedCorner/Program.cs

@ -1,111 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace AvatarWithRoundedCorner
{
static class Program
{
static void Main(string[] args)
{
System.IO.Directory.CreateDirectory("output");
using (var img = Image.Load("fb.jpg"))
{
// as generate returns a new IImage make sure we dispose of it
using (Image<Rgba32> destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 20)))
{
destRound.Save("output/fb.png");
}
using (Image<Rgba32> destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 100)))
{
destRound.Save("output/fb-round.png");
}
using (Image<Rgba32> destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 150)))
{
destRound.Save("output/fb-rounder.png");
}
using (Image<Rgba32> destRound = img.CloneAndConvertToAvatarWithoutApply(new Size(200, 200), 150))
{
destRound.Save("output/fb-rounder-without-apply.png");
}
// the original `img` object has not been altered at all.
}
}
// 1. The short way:
// Implements a full image mutating pipeline operating on IImageProcessingContext<Rgba32>
// We need the dimensions of the resized image to deduce 'IPathCollection' needed to build the corners,
// so we implement an "inline" image processor by utilizing 'ImageExtensions.Apply()'
private static IImageProcessingContext<Rgba32> ConvertToAvatar(this IImageProcessingContext<Rgba32> processingContext, Size size, float cornerRadius)
{
return processingContext.Resize(new ResizeOptions
{
Size = size,
Mode = ResizeMode.Crop
}).Apply(i => ApplyRoundedCorners(i, cornerRadius));
}
// 2. A more verbose way, avoiding 'Apply()':
// First we create a resized clone of the image, then we draw the corners on that instance with Mutate().
private static Image<Rgba32> CloneAndConvertToAvatarWithoutApply(this Image<Rgba32> image, Size size, float cornerRadius)
{
Image<Rgba32> result = image.Clone(
ctx => ctx.Resize(
new ResizeOptions
{
Size = size,
Mode = ResizeMode.Crop
}));
ApplyRoundedCorners(result, cornerRadius);
return result;
}
// This method can be seen as an inline implementation of an `IImageProcessor`:
// (The combination of `IImageOperations.Apply()` + this could be replaced with an `IImageProcessor`)
public static void ApplyRoundedCorners(Image<Rgba32> img, float cornerRadius)
{
IPathCollection corners = BuildCorners(img.Width, img.Height, cornerRadius);
// mutating in here as we already have a cloned original
img.Mutate(x => x.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true)
{
BlenderMode = PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background
}));
}
public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius)
{
// first create a square
var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius);
// then cut out of the square a circle so we are left with a corner
IPath cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius));
// corner is now a corner shape positions top left
//lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image
var center = new Vector2(imageWidth / 2F, imageHeight / 2F);
float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1;
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1;
// move it across the widthof the image - the width of the shape
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0);
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos);
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos);
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
}
}
}

3
samples/AvatarWithRoundedCorner/fb.jpg

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

12
samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj

@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
</Project>

23
samples/ChangeDefaultEncoderOptions/Program.cs

@ -1,23 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace ChangeDefaultEncoderOptions
{
class Program
{
static void Main(string[] args)
{
// lets switch out the default encoder for jpeg to one
// that saves at 90 quality and ignores the matadata
Configuration.Default.SetEncoder(ImageFormats.Jpeg, new JpegEncoder()
{
Quality = 90,
IgnoreMetadata = true
});
}
}
}

13
src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs

@ -122,24 +122,27 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
// Create a span for colors
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
using (IBuffer<float> amountBuffer = this.Target.MemoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = this.Target.MemoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(sourceY);
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX];
overlay[i] = pixel;
overlaySpan[i] = pixel;
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(this.source.MemoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

15
src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs

@ -152,19 +152,24 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
internal override void Apply(Span<float> scanline, int x, int y)
{
int patternY = y % this.pattern.Height;
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
MemoryManager memoryManager = this.Target.MemoryManager;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
int patternX = (x + i) % this.pattern.Width;
overlay[i] = this.pattern[patternY, patternX];
overlaySpan[i] = this.pattern[patternY, patternX];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

15
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -65,21 +65,26 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors
/// <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)
{
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
MemoryManager memoryManager = this.Target.MemoryManager;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
for (int i = 0; i < scanline.Length; i++)
{
if (this.Options.BlendPercentage < 1)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
overlay[i] = this[x + i, y];
overlaySpan[i] = this[x + i, y];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

15
src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs

@ -144,22 +144,27 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
using (var amountBuffer = new Buffer<float>(scanline.Length))
using (var overlay = new Buffer<TPixel>(scanline.Length))
MemoryManager memoryManager = this.Target.MemoryManager;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryManager.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.Span;
Span<TPixel> overlaySpan = overlay.Span;
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int offsetX = x + i;
// no doubt this one can be optermised further but I can't imagine its
// actually being used and can probably be removed/interalised for now
overlay[i] = this[offsetX, y];
overlaySpan[i] = this[offsetX, y];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}

34
src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs

@ -61,17 +61,14 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
public SolidBrushApplicator(ImageFrame<TPixel> source, TPixel color, GraphicsOptions options)
: base(source, options)
{
this.Colors = new Buffer<TPixel>(source.Width);
for (int i = 0; i < this.Colors.Length; i++)
{
this.Colors[i] = color;
}
this.Colors = source.MemoryManager.Allocate<TPixel>(source.Width);
this.Colors.Span.Fill(color);
}
/// <summary>
/// Gets the colors.
/// </summary>
protected Buffer<TPixel> Colors { get; }
protected IBuffer<TPixel> Colors { get; }
/// <summary>
/// Gets the color for a single pixel.
@ -81,7 +78,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] => this.Colors[x];
internal override TPixel this[int x, int y] => this.Colors.Span[x];
/// <inheritdoc />
public override void Dispose()
@ -92,23 +89,20 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
/// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y)
{
try
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
MemoryManager memoryManager = this.Target.MemoryManager;
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
{
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
Span<float> amountSpan = amountBuffer.Span;
using (var amountBuffer = new Buffer<float>(scanline.Length))
for (int i = 0; i < scanline.Length; i++)
{
for (int i = 0; i < scanline.Length; i++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
}
this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer);
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
}
catch (Exception)
{
throw;
this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan);
}
}
}

53
src/ImageSharp.Drawing/DrawImage.cs

@ -18,24 +18,13 @@ namespace SixLabors.ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Size size, Point location, GraphicsOptions options)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
if (size == default(Size))
{
size = new Size(image.Width, image.Height);
}
if (location == default(Point))
{
location = Point.Empty;
}
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, options));
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, options));
return source;
}
@ -45,14 +34,14 @@ namespace SixLabors.ImageSharp
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Blend<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float percent)
public static IImageProcessingContext<TPixel> Blend<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float opacity)
where TPixel : struct, IPixel<TPixel>
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlendPercentage = percent;
return DrawImage(source, image, default(Size), default(Point), options);
options.BlendPercentage = opacity;
return DrawImage(source, image, Point.Empty, options);
}
/// <summary>
@ -62,15 +51,15 @@ namespace SixLabors.ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="blender">The blending mode.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Blend<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent)
public static IImageProcessingContext<TPixel> Blend<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float opacity)
where TPixel : struct, IPixel<TPixel>
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlendPercentage = percent;
options.BlendPercentage = opacity;
options.BlenderMode = blender;
return DrawImage(source, image, default(Size), default(Point), options);
return DrawImage(source, image, Point.Empty, options);
}
/// <summary>
@ -79,12 +68,12 @@ namespace SixLabors.ImageSharp
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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 belnding amount.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Blend<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return DrawImage(source, image, default(Size), default(Point), options);
return DrawImage(source, image, Point.Empty, options);
}
/// <summary>
@ -93,16 +82,15 @@ namespace SixLabors.ImageSharp
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float percent, Size size, Point location)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float opacity, Point location)
where TPixel : struct, IPixel<TPixel>
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlendPercentage = percent;
return source.DrawImage(image, size, location, options);
options.BlendPercentage = opacity;
return source.DrawImage(image, location, options);
}
/// <summary>
@ -112,17 +100,16 @@ namespace SixLabors.ImageSharp
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="blender">The type of bending to apply.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent, Size size, Point location)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float opacity, Point location)
where TPixel : struct, IPixel<TPixel>
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlenderMode = blender;
options.BlendPercentage = percent;
return source.DrawImage(image, size, location, options);
options.BlendPercentage = opacity;
return source.DrawImage(image, location, options);
}
}
}

6
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -36,9 +36,9 @@
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0004" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0003" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0003" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-ci0005" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-ci0005" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-ci0005" />
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>All</PrivateAssets>

2
src/ImageSharp.Drawing/Paths/ShapePath.cs

@ -4,6 +4,8 @@
using System;
using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Drawing

17
src/ImageSharp.Drawing/Paths/ShapeRegion.cs

@ -46,18 +46,17 @@ namespace SixLabors.ImageSharp.Drawing
{
var start = new PointF(this.Bounds.Left - 1, y);
var end = new PointF(this.Bounds.Right + 1, y);
using (var innerBuffer = new Buffer<PointF>(buffer.Length))
{
PointF[] array = innerBuffer.Array;
int count = this.Shape.FindIntersections(start, end, array, 0);
for (int i = 0; i < count; i++)
{
buffer[i + offset] = array[i].X;
}
// TODO: This is a temporary workaround because of the lack of Span<T> API-s on IPath. We should use MemoryManager.Allocate() here!
PointF[] innerBuffer = new PointF[buffer.Length];
int count = this.Shape.FindIntersections(start, end, innerBuffer, 0);
return count;
for (int i = 0; i < count; i++)
{
buffer[i + offset] = innerBuffer[i].X;
}
return count;
}
}
}

86
src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs

@ -19,93 +19,77 @@ namespace SixLabors.ImageSharp.Drawing.Processors
internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PixelBlender<TPixel> blender;
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The opacity of the image to blend. Between 0 and 100.</param>
public DrawImageProcessor(Image<TPixel> image, Size size, Point location, GraphicsOptions options)
/// <param name="options">The opacity of the image to blend. Between 0 and 1.</param>
public DrawImageProcessor(Image<TPixel> image, Point location, GraphicsOptions options)
{
Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage));
this.Image = image;
this.Size = size;
this.Alpha = options.BlendPercentage;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.Opacity = options.BlendPercentage;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.Location = location;
}
/// <summary>
/// Gets the image to blend.
/// Gets the image to blend
/// </summary>
public Image<TPixel> Image { get; }
/// <summary>
/// Gets the alpha percentage value.
/// Gets the opacity of the image to blend
/// </summary>
public float Alpha { get; }
public float Opacity { get; }
/// <summary>
/// Gets the size to draw the blended image.
/// Gets the pixel blender
/// </summary>
public Size Size { get; }
public PixelBlender<TPixel> Blender { get; }
/// <summary>
/// Gets the location to draw the blended image.
/// Gets the location to draw the blended image
/// </summary>
public Point Location { get; }
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
Image<TPixel> disposableImage = null;
Image<TPixel> targetImage = this.Image;
PixelBlender<TPixel> blender = this.Blender;
int locationY = this.Location.Y;
try
{
if (targetImage.Size() != this.Size)
{
targetImage = disposableImage = this.Image.Clone(x => x.Resize(this.Size.Width, this.Size.Height));
}
// Align start/end positions.
Rectangle bounds = targetImage.Bounds();
// 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.Width);
maxX = Math.Min(this.Location.X + this.Size.Width, maxX);
int targetX = minX - this.Location.X;
int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
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 minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
maxY = Math.Min(this.Location.Y + this.Size.Height, maxY);
int width = maxX - minX;
int width = maxX - minX;
using (var amount = new Buffer<float>(width))
{
for (int i = 0; i < width; i++)
{
amount[i] = this.Alpha;
}
MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager;
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width);
this.blender.Blend(background, background, foreground, amount);
});
}
}
finally
using (IBuffer<float> amount = memoryManager.Allocate<float>(width))
{
disposableImage?.Dispose();
amount.Span.Fill(this.Opacity);
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend(memoryManager, background, background, foreground, amount.Span);
});
}
}
}

24
src/ImageSharp.Drawing/Processors/FillProcessor.cs

@ -66,25 +66,25 @@ namespace SixLabors.ImageSharp.Drawing.Processors
int width = maxX - minX;
using (var amount = new Buffer<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
using (IBuffer<float> amount = source.MemoryManager.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
for (int i = 0; i < width; i++)
{
amount[i] = this.options.BlendPercentage;
}
amount.Span.Fill(this.options.BlendPercentage);
Parallel.For(
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;
int offsetX = minX - startX;
{
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount, offsetX, offsetY);
});
applicator.Apply(amount.Span, offsetX, offsetY);
});
}
}
}

129
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -78,8 +78,6 @@ namespace SixLabors.ImageSharp.Drawing.Processors
return; // no effect inside image;
}
ArrayPool<float> arrayPool = ArrayPool<float>.Shared;
int maxIntersections = region.MaxIntersections;
float subpixelCount = 4;
@ -100,101 +98,94 @@ namespace SixLabors.ImageSharp.Drawing.Processors
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX;
using (var scanline = new Buffer<float>(scanlineWidth))
using (BasicArrayBuffer<float> buffer = source.MemoryManager.AllocateFake<float>(maxIntersections))
using (BasicArrayBuffer<float> scanline = source.MemoryManager.AllocateFake<float>(scanlineWidth))
{
try
bool scanlineDirty = true;
for (int y = minY; y < maxY; y++)
{
bool scanlineDirty = true;
for (int y = minY; y < maxY; y++)
if (scanlineDirty)
{
if (scanlineDirty)
// clear the buffer
for (int x = 0; x < scanlineWidth; x++)
{
// clear the buffer
for (int x = 0; x < scanlineWidth; x++)
{
scanline[x] = 0;
}
scanlineDirty = false;
scanline[x] = 0;
}
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer, 0);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
}
scanlineDirty = false;
}
QuickSort(new Span<float>(buffer, 0, pointsFound));
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer.Array, 0);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
}
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
QuickSort(new Span<float>(buffer.Array, 0, pointsFound));
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
if (endX >= 0 && endX < scanline.Length)
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
scanline[startX] += 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++)
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[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 (scanlineDirty)
{
if (!this.Options.Antialias)
{
if (!this.Options.Antialias)
for (int x = 0; x < scanlineWidth; x++)
{
for (int x = 0; x < scanlineWidth; x++)
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
}
else
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
}
else
{
scanline[x] = 0;
}
scanline[x] = 0;
}
}
applicator.Apply(scanline, minX, y);
}
applicator.Apply(scanline.Span, minX, y);
}
}
finally
{
arrayPool.Return(buffer);
}
}
}
}

11
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -88,6 +89,14 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(row);
/// <summary>
/// Gets the <see cref="MemoryManager"/> assigned to 'source'.
/// </summary>
/// <param name="source">The source image</param>
/// <returns>Returns the configuration.</returns>
internal static MemoryManager GetMemoryManager(this IConfigurable source)
=> GetConfiguration(source).MemoryManager;
/// <summary>
/// Gets the span to the backing buffer.
/// </summary>
@ -140,6 +149,6 @@ namespace SixLabors.ImageSharp.Advanced
/// <returns>A reference to the element.</returns>
private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> ref source.PixelBuffer.Span.DangerousGetPinnableReference();
=> ref MemoryMarshal.GetReference(source.PixelBuffer.Span);
}
}

1
src/ImageSharp/ApplyProcessors.cs

@ -4,7 +4,6 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp
{

2
src/ImageSharp/ColorSpaces/CieLab.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents a CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
internal struct CieLab : IColorVector, IEquatable<CieLab>, IAlmostEquatable<CieLab, float>
internal readonly struct CieLab : IColorVector, IEquatable<CieLab>, IAlmostEquatable<CieLab, float>
{
/// <summary>
/// D50 standard illuminant.

2
src/ImageSharp/ColorSpaces/CieLch.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC"/>
/// </summary>
internal struct CieLch : IColorVector, IEquatable<CieLch>, IAlmostEquatable<CieLch, float>
internal readonly struct CieLch : IColorVector, IEquatable<CieLch>, IAlmostEquatable<CieLch, float>
{
/// <summary>
/// D50 standard illuminant.

2
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CieLchuv_or_CIEHLC"/>
/// </summary>
internal struct CieLchuv : IColorVector, IEquatable<CieLchuv>, IAlmostEquatable<CieLchuv, float>
internal readonly struct CieLchuv : IColorVector, IEquatable<CieLchuv>, IAlmostEquatable<CieLchuv, float>
{
/// <summary>
/// D50 standard illuminant.

2
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// attempted perceptual uniformity
/// <see href="https://en.wikipedia.org/wiki/CIELUV"/>
/// </summary>
internal struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float>
internal readonly struct CieLuv : IColorVector, IEquatable<CieLuv>, IAlmostEquatable<CieLuv, float>
{
/// <summary>
/// D65 standard illuminant.

6
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -6,12 +6,13 @@ using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
// ReSharper disable CompareOfFloatsByEqualityOperator
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents the coordinates of CIEXY chromaticity space
/// </summary>
internal struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>, IAlmostEquatable<CieXyChromaticityCoordinates, float>
internal readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>, IAlmostEquatable<CieXyChromaticityCoordinates, float>
{
/// <summary>
/// Represents a <see cref="CieXyChromaticityCoordinates"/> that has X, Y values set to zero.
@ -143,7 +144,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieXyChromaticityCoordinates other)
{
return this.backingVector.Equals(other.backingVector);
// The memberwise comparison here is a workaround for https://github.com/dotnet/coreclr/issues/16443
return this.X == other.X && this.Y == other.Y;
}
/// <inheritdoc/>

2
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE xyY 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space"/>
/// </summary>
internal struct CieXyy : IColorVector, IEquatable<CieXyy>, IAlmostEquatable<CieXyy, float>
internal readonly struct CieXyy : IColorVector, IEquatable<CieXyy>, IAlmostEquatable<CieXyy, float>
{
/// <summary>
/// Represents a <see cref="CieXyy"/> that has X, Y, and Y values set to zero.

2
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE XYZ 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space#Definition_of_the_CIE_XYZ_color_space"/>
/// </summary>
internal struct CieXyz : IColorVector, IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float>
internal readonly struct CieXyz : IColorVector, IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float>
{
/// <summary>
/// Represents a <see cref="CieXyz"/> that has X, Y, and Z values set to zero.

2
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
/// </summary>
internal struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float>
internal readonly struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float>
{
/// <summary>
/// Represents a <see cref="Cmyk"/> that has C, M, Y, and K values set to zero.

3
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs

@ -184,8 +184,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
Rgb rgb = YCbCrAndRgbConverter.Convert(color);
// Adaptation
// TODO: Check this!
return rgb.WorkingSpace.Equals(this.TargetRgbWorkingSpace) ? rgb : this.Adapt(rgb);
return this.Adapt(rgb);
}
}
}

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
internal abstract class LinearRgbAndCieXyzConverterBase
{
/// <summary>
/// Geturns the correct matrix to convert between the Rgb and CieXyz color space.
/// Returns the correct matrix to convert between the Rgb and CieXyz color space.
/// </summary>
/// <param name="workingSpace">The Rgb working space.</param>
/// <returns>The <see cref="Matrix4x4"/> based on the chromaticity and working space.</returns>
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix);
// Use transposed Rows/Coloumns
// Use transposed Rows/Columns
// TODO: Is there a built in method for this multiplication?
return new Matrix4x4
{

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs

@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
public CieXyz Convert(LinearRgb input)
{
DebugGuard.NotNull(input, nameof(input));
Guard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix);
return new CieXyz(vector);

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <summary>
/// Trivial implementation of <see cref="IRgbWorkingSpace"/>
/// </summary>
internal struct RgbWorkingSpace : IRgbWorkingSpace
internal readonly struct RgbWorkingSpace : IRgbWorkingSpace
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbWorkingSpace"/> struct.

2
src/ImageSharp/ColorSpaces/Hsl.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a Hsl (hue, saturation, lightness) color.
/// </summary>
internal struct Hsl : IColorVector, IEquatable<Hsl>, IAlmostEquatable<Hsl, float>
internal readonly struct Hsl : IColorVector, IEquatable<Hsl>, IAlmostEquatable<Hsl, float>
{
/// <summary>
/// Represents a <see cref="Hsl"/> that has H, S, and L values set to zero.

2
src/ImageSharp/ColorSpaces/Hsv.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
/// </summary>
internal struct Hsv : IColorVector, IEquatable<Hsv>, IAlmostEquatable<Hsv, float>
internal readonly struct Hsv : IColorVector, IEquatable<Hsv>, IAlmostEquatable<Hsv, float>
{
/// <summary>
/// Represents a <see cref="Hsv"/> that has H, S, and V values set to zero.

2
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an Hunter LAB color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
internal struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float>
internal readonly struct HunterLab : IColorVector, IEquatable<HunterLab>, IAlmostEquatable<HunterLab, float>
{
/// <summary>
/// D50 standard illuminant.

2
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents an linear Rgb color with specified <see cref="IRgbWorkingSpace"/> working space
/// </summary>
internal struct LinearRgb : IColorVector, IEquatable<LinearRgb>, IAlmostEquatable<LinearRgb, float>
internal readonly struct LinearRgb : IColorVector, IEquatable<LinearRgb>, IAlmostEquatable<LinearRgb, float>
{
/// <summary>
/// Represents a <see cref="LinearRgb"/> that has R, G, and B values set to zero.

2
src/ImageSharp/ColorSpaces/Lms.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
/// <see href="https://en.wikipedia.org/wiki/LMS_color_space"/>
/// </summary>
internal struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float>
internal readonly struct Lms : IColorVector, IEquatable<Lms>, IAlmostEquatable<Lms, float>
{
/// <summary>
/// Represents a <see cref="Lms"/> that has L, M, and S values set to zero.

2
src/ImageSharp/ColorSpaces/Rgb.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents an RGB color with specified <see cref="IRgbWorkingSpace"/> working space
/// </summary>
internal struct Rgb : IColorVector, IEquatable<Rgb>, IAlmostEquatable<Rgb, float>
internal readonly struct Rgb : IColorVector, IEquatable<Rgb>, IAlmostEquatable<Rgb, float>
{
/// <summary>
/// Represents a <see cref="Rgb"/> that has R, G, and B values set to zero.

2
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// <see href="http://www.ijg.org/files/T-REC-T.871-201105-I!!PDF-E.pdf"/>
/// </summary>
internal struct YCbCr : IColorVector, IEquatable<YCbCr>, IAlmostEquatable<YCbCr, float>
internal readonly struct YCbCr : IColorVector, IEquatable<YCbCr>, IAlmostEquatable<YCbCr, float>
{
/// <summary>
/// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero.

14
src/ImageSharp/Common/Extensions/ComparableExtensions.cs

@ -169,19 +169,5 @@ namespace SixLabors.ImageSharp
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Swaps the references to two objects in memory.
/// </summary>
/// <param name="first">The first reference.</param>
/// <param name="second">The second reference.</param>
/// <typeparam name="T">The type of object.</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Swap<T>(ref T first, ref T second)
{
T temp = second;
second = first;
first = temp;
}
}
}

12
src/ImageSharp/Common/Extensions/SimdUtils.cs

@ -76,14 +76,14 @@ namespace SixLabors.ImageSharp
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref source.DangerousGetPinnableReference());
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref dest.DangerousGetPinnableReference());
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporal struct, because
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
@ -117,14 +117,14 @@ namespace SixLabors.ImageSharp
return;
}
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref source.DangerousGetPinnableReference());
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref dest.DangerousGetPinnableReference());
ref Vector<float> srcBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporal struct, because
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);

21
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -29,23 +29,16 @@ namespace SixLabors.ImageSharp
}
else
{
byte[] foo = ArrayPool<byte>.Shared.Rent(count);
try
byte[] foo = new byte[count];
while (count > 0)
{
while (count > 0)
int bytesRead = stream.Read(foo, 0, count);
if (bytesRead == 0)
{
int bytesRead = stream.Read(foo, 0, count);
if (bytesRead == 0)
{
break;
}
count -= bytesRead;
break;
}
}
finally
{
ArrayPool<byte>.Shared.Return(foo);
count -= bytesRead;
}
}
}

28
src/ImageSharp/Common/Extensions/Vector4Extensions.cs

@ -12,6 +12,34 @@ namespace SixLabors.ImageSharp
/// </summary>
internal static class Vector4Extensions
{
/// <summary>
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
/// </summary>
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
/// <returns>The <see cref="Vector4"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Premultiply(this Vector4 source)
{
float w = source.W;
Vector4 premultiplied = source * w;
premultiplied.W = w;
return premultiplied;
}
/// <summary>
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(Vector4)"/>.
/// </summary>
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
/// <returns>The <see cref="Vector4"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 UnPremultiply(this Vector4 source)
{
float w = source.W;
Vector4 unpremultiplied = source / w;
unpremultiplied.W = w;
return unpremultiplied;
}
/// <summary>
/// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>

21
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -139,27 +139,6 @@ namespace SixLabors.ImageSharp
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
/// <summary>
/// Gets the bounding <see cref="Rectangle"/> from the given matrix.
/// </summary>
/// <param name="rectangle">The source rectangle.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix)
{
var leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix);
var rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix);
var leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix);
var rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom };
float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min();
float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min();
return new Rectangle(0, 0, (int)extentX, (int)extentY);
}
/// <summary>
/// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one.

60
src/ImageSharp/Common/Helpers/ParallelFor.cs

@ -0,0 +1,60 @@
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Utility methods for Parallel.For() execution. Use this instead of raw <see cref="Parallel"/> calls!
/// </summary>
internal static class ParallelFor
{
/// <summary>
/// Helper method to execute Parallel.For using the settings in <see cref="Configuration.ParallelOptions"/>
/// </summary>
public static void WithConfiguration(int fromInclusive, int toExclusive, Configuration configuration, Action<int> body)
{
Parallel.For(fromInclusive, toExclusive, configuration.ParallelOptions, body);
}
/// <summary>
/// Helper method to execute Parallel.For with temporary worker buffer shared between executing tasks.
/// The buffer is not guaranteed to be clean!
/// </summary>
/// <typeparam name="T">The value type of the buffer</typeparam>
/// <param name="fromInclusive">The start index, inclusive.</param>
/// <param name="toExclusive">The end index, exclusive.</param>
/// <param name="configuration">The <see cref="Configuration"/> used for getting the <see cref="MemoryManager"/> and <see cref="ParallelOptions"/></param>
/// <param name="bufferLength">The length of the requested parallel buffer</param>
/// <param name="body">The delegate that is invoked once per iteration.</param>
public static void WithTemporaryBuffer<T>(
int fromInclusive,
int toExclusive,
Configuration configuration,
int bufferLength,
Action<int, IBuffer<T>> body)
where T : struct
{
MemoryManager memoryManager = configuration.MemoryManager;
ParallelOptions parallelOptions = configuration.ParallelOptions;
IBuffer<T> InitBuffer()
{
return memoryManager.Allocate<T>(bufferLength);
}
void CleanUpBuffer(IBuffer<T> buffer)
{
buffer.Dispose();
}
IBuffer<T> BodyFunc(int i, ParallelLoopState state, IBuffer<T> buffer)
{
body(i, buffer);
return buffer;
}
Parallel.For(fromInclusive, toExclusive, parallelOptions, InitBuffer, BodyFunc, CleanUpBuffer);
}
}
}

406
src/ImageSharp/Configuration.cs

@ -1,261 +1,145 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public sealed class Configuration
{
/// <summary>
/// A lazily initialized configuration default instance.
/// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
public Configuration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IConfigurationModule[] configurationModules)
{
if (configurationModules != null)
{
foreach (IConfigurationModule p in configurationModules)
{
p.Configure(this);
}
}
}
/// <summary>
/// Gets the default <see cref="Configuration"/> instance.
/// </summary>
public static Configuration Default { get; } = Lazy.Value;
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the filesystem helper for accessing the local file system.
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
#endif
/// <summary>
/// Gets or sets the image operations provider factory.
/// </summary>
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
{
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
}
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
this.imageFormats.Add(format);
}
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtension(string extension)
{
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary>
/// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType)
{
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Sets a specific image encoder as the encoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
}
/// <summary>
/// Sets a specific image decoder as the decoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Removes all the registered image format detectors.
/// </summary>
public void ClearImageFormatDetectors()
{
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
}
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector)
{
Guard.NotNull(detector, nameof(detector));
this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
return decoder;
}
return null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
return null;
}
/// <summary>
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// <para><see cref="PngConfigurationModule"/></para>
/// <para><see cref="JpegConfigurationModule"/></para>
/// <para><see cref="GifConfigurationModule"/></para>
/// <para><see cref="BmpConfigurationModule"/></para>
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/></returns>
internal static Configuration CreateDefaultInstance()
{
return new Configuration(
new PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public sealed class Configuration
{
/// <summary>
/// A lazily initialized configuration default instance.
/// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
public Configuration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IConfigurationModule[] configurationModules)
{
if (configurationModules != null)
{
foreach (IConfigurationModule p in configurationModules)
{
p.Configure(this);
}
}
}
/// <summary>
/// Gets the default <see cref="Configuration"/> instance.
/// </summary>
public static Configuration Default { get; } = Lazy.Value;
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.ImageFormatsManager.ImageFormats;
/// <summary>
/// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source.
/// </summary>
public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current;
/// <summary>
/// Gets or sets the <see cref="ImageFormatManager"/> that is currently in use.
/// </summary>
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();
/// <summary>
/// Gets or sets the <see cref="MemoryManager"/> that is currently in use.
/// </summary>
public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault();
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize;
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the filesystem helper for accessing the local file system.
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
#endif
/// <summary>
/// Gets or sets the image operations provider factory.
/// </summary>
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
{
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
}
/// <summary>
/// Creates a shallow copy of the <see cref="Configuration"/>
/// </summary>
/// <returns>A new configuration instance</returns>
public Configuration ShallowCopy()
{
return new Configuration
{
ParallelOptions = this.ParallelOptions,
ImageFormatsManager = this.ImageFormatsManager,
MemoryManager = this.MemoryManager,
ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin,
#if !NETSTANDARD1_1
FileSystem = this.FileSystem
#endif
};
}
/// <summary>
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// <para><see cref="PngConfigurationModule"/></para>
/// <para><see cref="JpegConfigurationModule"/></para>
/// <para><see cref="GifConfigurationModule"/></para>
/// <para><see cref="BmpConfigurationModule"/></para>
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/></returns>
internal static Configuration CreateDefaultInstance()
{
return new Configuration(
new PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
}
}
}

19
src/ImageSharp/DefaultInternalImageProcessorContext.cs

@ -1,7 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
@ -34,6 +36,9 @@ namespace SixLabors.ImageSharp
}
}
/// <inheritdoc/>
public MemoryManager MemoryManager => this.source.GetConfiguration().MemoryManager;
/// <inheritdoc/>
public Image<TPixel> Apply()
{
@ -46,14 +51,17 @@ namespace SixLabors.ImageSharp
return this.destination;
}
/// <inheritdoc/>
public Size GetCurrentSize() => this.GetCurrentBounds().Size;
/// <inheritdoc/>
public IImageProcessingContext<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor, Rectangle rectangle)
{
if (!this.mutate && this.destination == null)
{
// This will only work if the first processor applied is the cloning one thus
// realistically for this optermissation to work the resize must the first processor
// applied any only up processors will take the douple data path.
// realistically for this optimization to work the resize must the first processor
// applied any only up processors will take the double data path.
if (processor is ICloningImageProcessor<TPixel> cloningImageProcessor)
{
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle);
@ -70,7 +78,12 @@ namespace SixLabors.ImageSharp
/// <inheritdoc/>
public IImageProcessingContext<TPixel> ApplyProcessor(IImageProcessor<TPixel> processor)
{
return this.ApplyProcessor(processor, this.source.Bounds());
return this.ApplyProcessor(processor, this.GetCurrentBounds());
}
private Rectangle GetCurrentBounds()
{
return this.destination?.Bounds() ?? this.source.Bounds();
}
}
}

24
src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs

@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
this.startingOffset = 0;
for (int i = 0; i < this.matrixWidth; i++)
{
// Good to disable here as we are not comparing matematical output.
// Good to disable here as we are not comparing mathematical output.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (matrix[0, i] != 0)
{
@ -70,27 +70,15 @@ namespace SixLabors.ImageSharp.Dithering.Base
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(ImageFrame<TPixel> pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
where TPixel : struct, IPixel<TPixel>
{
this.Dither(pixels, source, transformed, x, y, minX, minY, maxX, maxY, true);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
where TPixel : struct, IPixel<TPixel>
{
if (replacePixel)
{
// Assign the transformed pixel to the array.
image[x, y] = transformed;
}
image[x, y] = transformed;
// Calculate the error
Vector4 error = source.ToVector4() - transformed.ToVector4();
// Loop through and distribute the error amongst neighbouring pixels.
// Loop through and distribute the error amongst neighboring pixels.
for (int row = 0; row < this.matrixHeight; row++)
{
int matrixY = y + row;
@ -115,10 +103,8 @@ namespace SixLabors.ImageSharp.Dithering.Base
ref TPixel pixel = ref rowSpan[matrixX];
var offsetColor = pixel.ToVector4();
var coefficientVector = new Vector4(coefficient);
Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor;
result.W = offsetColor.W;
Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor;
pixel.PackFromVector4(result);
}
}

24
src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Encapsulates properties and methods required to perfom diffused error dithering on an image.
/// Encapsulates properties and methods required to perform diffused error dithering on an image.
/// </summary>
public interface IErrorDiffuser
{
@ -25,25 +25,5 @@ namespace SixLabors.ImageSharp.Dithering
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Transforms the image applying the dither matrix. This method alters the input pixels array
/// </summary>
/// <param name="image">The image</param>
/// <param name="source">The source pixel</param>
/// <param name="transformed">The transformed pixel</param>
/// <param name="x">The column index.</param>
/// <param name="y">The row index.</param>
/// <param name="minX">The minimum column value.</param>
/// <param name="minY">The minimum row value.</param>
/// <param name="maxX">The maximum column value.</param>
/// <param name="maxY">The maximum row value.</param>
/// <param name="replacePixel">
/// Whether to replace the pixel at the given coordinates with the transformed value.
/// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false.
/// </param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
where TPixel : struct, IPixel<TPixel>;
}
}
}

56
src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs

@ -0,0 +1,56 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Contains reusable static instances of known error diffusion algorithms
/// </summary>
public static class KnownDiffusers
{
/// <summary>
/// Gets the error diffuser that implements the Atkinson algorithm.
/// </summary>
public static IErrorDiffuser Atkinson { get; } = new AtkinsonDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Burks algorithm.
/// </summary>
public static IErrorDiffuser Burks { get; } = new BurksDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Floyd-Steinberg algorithm.
/// </summary>
public static IErrorDiffuser FloydSteinberg { get; } = new FloydSteinbergDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Jarvis-Judice-Ninke algorithm.
/// </summary>
public static IErrorDiffuser JarvisJudiceNinke { get; } = new JarvisJudiceNinkeDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Sierra-2 algorithm.
/// </summary>
public static IErrorDiffuser Sierra2 { get; } = new Sierra2Diffuser();
/// <summary>
/// Gets the error diffuser that implements the Sierra-3 algorithm.
/// </summary>
public static IErrorDiffuser Sierra3 { get; } = new Sierra3Diffuser();
/// <summary>
/// Gets the error diffuser that implements the Sierra-Lite algorithm.
/// </summary>
public static IErrorDiffuser SierraLite { get; } = new SierraLiteDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Stevenson-Arce algorithm.
/// </summary>
public static IErrorDiffuser StevensonArce { get; } = new StevensonArceDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Stucki algorithm.
/// </summary>
public static IErrorDiffuser Stucki { get; } = new StuckiDiffuser();
}
}

34
src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Dithering.Base;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
/// </summary>
public sealed class StevensonArceDiffuser : ErrorDiffuserBase
{
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> StevensonArceMatrix =
new float[,]
{
{ 0, 0, 0, 0, 0, 32, 0 },
{ 12, 0, 26, 0, 30, 0, 16 },
{ 0, 12, 0, 26, 0, 12, 0 },
{ 5, 0, 12, 0, 12, 0, 5 }
};
/// <summary>
/// Initializes a new instance of the <see cref="StevensonArceDiffuser"/> class.
/// </summary>
public StevensonArceDiffuser()
: base(StevensonArceMatrix, 200)
{
}
}
}

36
src/ImageSharp/Dithering/Ordered/BayerDither.cs

@ -1,36 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Dithering.Base;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix.
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
/// </summary>
public sealed class BayerDither : OrderedDitherBase
{
/// <summary>
/// The threshold matrix.
/// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1
/// </summary>
private static readonly Fast2DArray<byte> ThresholdMatrix =
new byte[,]
{
{ 15, 143, 47, 175 },
{ 207, 79, 239, 111 },
{ 63, 191, 31, 159 },
{ 255, 127, 223, 95 }
};
/// <summary>
/// Initializes a new instance of the <see cref="BayerDither"/> class.
/// </summary>
public BayerDither()
: base(ThresholdMatrix)
{
}
}
}

19
src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies order dithering using the 2x2 Bayer dithering matrix.
/// </summary>
public sealed class BayerDither2x2 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="BayerDither2x2"/> class.
/// </summary>
public BayerDither2x2()
: base(2)
{
}
}
}

19
src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies order dithering using the 4x4 Bayer dithering matrix.
/// </summary>
public sealed class BayerDither4x4 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="BayerDither4x4"/> class.
/// </summary>
public BayerDither4x4()
: base(4)
{
}
}
}

19
src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies order dithering using the 8x8 Bayer dithering matrix.
/// </summary>
public sealed class BayerDither8x8 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="BayerDither8x8"/> class.
/// </summary>
public BayerDither8x8()
: base(8)
{
}
}
}

7
src/ImageSharp/Dithering/Ordered/IOrderedDither.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Encapsulates properties and methods required to perfom ordered dithering on an image.
/// Encapsulates properties and methods required to perform ordered dithering on an image.
/// </summary>
public interface IOrderedDither
{
@ -17,12 +17,11 @@ namespace SixLabors.ImageSharp.Dithering
/// <param name="source">The source pixel</param>
/// <param name="upper">The color to apply to the pixels above the threshold.</param>
/// <param name="lower">The color to apply to the pixels below the threshold.</param>
/// <param name="rgba">The <see cref="Rgba32"/> to pack/unpack to.</param>
/// <param name="index">The component index to test the threshold against. Must range from 0 to 3.</param>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <param name="x">The column index.</param>
/// <param name="y">The row index.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y)
void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y)
where TPixel : struct, IPixel<TPixel>;
}
}

31
src/ImageSharp/Dithering/Ordered/KnownDitherers.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Contains reusable static instances of known ordered dither matrices
/// </summary>
public class KnownDitherers
{
/// <summary>
/// Gets the order ditherer using the 2x2 Bayer dithering matrix
/// </summary>
public static IOrderedDither BayerDither2x2 { get; } = new BayerDither2x2();
/// <summary>
/// Gets the order ditherer using the 3x3 dithering matrix
/// </summary>
public static IOrderedDither OrderedDither3x3 { get; } = new OrderedDither3x3();
/// <summary>
/// Gets the order ditherer using the 4x4 Bayer dithering matrix
/// </summary>
public static IOrderedDither BayerDither4x4 { get; } = new BayerDither4x4();
/// <summary>
/// Gets the order ditherer using the 8x8 Bayer dithering matrix
/// </summary>
public static IOrderedDither BayerDither8x8 { get; } = new BayerDither8x8();
}
}

50
src/ImageSharp/Dithering/Ordered/OrderedDither.cs

@ -1,36 +1,50 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Dithering.Base;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies error diffusion based dithering using the 4x4 ordered dithering matrix.
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
/// An ordered dithering matrix with equal sides of arbitrary length
/// </summary>
public sealed class OrderedDither : OrderedDitherBase
public class OrderedDither : IOrderedDither
{
/// <summary>
/// The threshold matrix.
/// This is calculated by multiplying each value in the original matrix by 16
/// </summary>
private static readonly Fast2DArray<byte> ThresholdMatrix =
new byte[,]
{
{ 0, 128, 32, 160 },
{ 192, 64, 224, 96 },
{ 48, 176, 16, 144 },
{ 240, 112, 208, 80 }
};
private readonly Fast2DArray<uint> thresholdMatrix;
private readonly int modulusX;
private readonly int modulusY;
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDither"/> class.
/// </summary>
public OrderedDither()
: base(ThresholdMatrix)
/// <param name="length">The length of the matrix sides</param>
public OrderedDither(uint length)
{
Fast2DArray<uint> ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length);
this.modulusX = ditherMatrix.Width;
this.modulusY = ditherMatrix.Height;
// Adjust the matrix range for 0-255
// It looks like it's actually possible to dither an image using it's own colors. We should investigate for V2
// https://stackoverflow.com/questions/12422407/monochrome-dithering-in-javascript-bayer-atkinson-floyd-steinberg
int multiplier = 256 / ditherMatrix.Count;
for (int y = 0; y < ditherMatrix.Height; y++)
{
for (int x = 0; x < ditherMatrix.Width; x++)
{
ditherMatrix[y, x] = (uint)((ditherMatrix[y, x] + 1) * multiplier) - 1;
}
}
this.thresholdMatrix = ditherMatrix;
}
/// <inheritdoc />
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y)
where TPixel : struct, IPixel<TPixel>
{
image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper;
}
}
}

19
src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// Applies order dithering using the 3x3 dithering matrix.
/// </summary>
public sealed class OrderedDither3x3 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDither3x3"/> class.
/// </summary>
public OrderedDither3x3()
: base(3)
{
}
}
}

53
src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs

@ -1,53 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering.Base
{
/// <summary>
/// The base class for performing ordered dithering using a 4x4 matrix.
/// </summary>
public abstract class OrderedDitherBase : IOrderedDither
{
/// <summary>
/// The dithering matrix
/// </summary>
private Fast2DArray<byte> matrix;
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDitherBase"/> class.
/// </summary>
/// <param name="matrix">The thresholding matrix. </param>
internal OrderedDitherBase(Fast2DArray<byte> matrix)
{
this.matrix = matrix;
}
/// <inheritdoc />
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y)
where TPixel : struct, IPixel<TPixel>
{
source.ToRgba32(ref rgba);
switch (index)
{
case 0:
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.R ? lower : upper;
return;
case 1:
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.G ? lower : upper;
return;
case 2:
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.B ? lower : upper;
return;
case 3:
image[x, y] = this.matrix[y % 3, x % 3] >= rgba.A ? lower : upper;
return;
}
throw new ArgumentOutOfRangeException(nameof(index), "Index should be between 0 and 3 inclusive.");
}
}
}

94
src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs

@ -0,0 +1,94 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Dithering
{
/// <summary>
/// A factory for creating ordered dither matrices.
/// </summary>
internal static class OrderedDitherFactory
{
/// <summary>
/// Creates an ordered dithering matrix with equal sides of arbitrary length.
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
/// </summary>
/// <param name="length">The length of the matrix sides</param>
/// <returns>The <see cref="Fast2DArray{T}"/></returns>
public static Fast2DArray<uint> CreateDitherMatrix(uint length)
{
// Calculate the the logarithm of length to the base 2
uint exponent = 0;
uint bayerLength = 0;
do
{
exponent++;
bayerLength = (uint)(1 << (int)exponent);
}
while (length > bayerLength);
// Create our Bayer matrix that matches the given exponent and dimensions
var matrix = new Fast2DArray<uint>((int)length);
uint i = 0;
for (int y = 0; y < length; y++)
{
for (int x = 0; x < length; x++)
{
matrix[y, x] = Bayer(i / length, i % length, exponent);
i++;
}
}
// If the user requested a matrix with a non-power-of-2 length e.g. 3x3 and we used 4x4 algorithm,
// we need to convert the numbers so that the resulting range is un-gapped.
// We generated: We saved: We compress the number range:
// 0 8 2 10 0 8 2 0 5 2
// 12 4 14 6 12 4 14 7 4 8
// 3 11 1 9 3 11 1 3 6 1
// 15 7 13 5
uint maxValue = bayerLength * bayerLength;
uint missing = 0;
for (uint v = 0; v < maxValue; ++v)
{
bool found = false;
for (int y = 0; y < length; ++y)
{
for (int x = 0; x < length; x++)
{
if (matrix[y, x] == v)
{
matrix[y, x] -= missing;
found = true;
break;
}
}
}
if (!found)
{
++missing;
}
}
return matrix;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Bayer(uint x, uint y, uint order)
{
uint result = 0;
for (uint i = 0; i < order; ++i)
{
uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1);
uint xOdd = x & 1;
result = ((result << 1 | xOdd_XOR_yOdd) << 1) | xOdd;
x >>= 1;
y >>= 1;
}
return result;
}
}
}

58
src/ImageSharp/Dithering/error_diffusion.txt

@ -0,0 +1,58 @@
List of error diffusion schemes.
Quantization error of *current* pixel is added to the pixels
on the right and below according to the formulas below.
This works nicely for most static pictures, but causes
an avalanche of jittering artifacts if used in animation.
Floyd-Steinberg:
* 7
3 5 1 / 16
Jarvis-Judice-Ninke:
* 7 5
3 5 7 5 3
1 3 5 3 1 / 48
Stucki:
* 8 4
2 4 8 4 2
1 2 4 2 1 / 42
Burkes:
* 8 4
2 4 8 4 2 / 32
Sierra3:
* 5 3
2 4 5 4 2
2 3 2 / 32
Sierra2:
* 4 3
1 2 3 2 1 / 16
Sierra-2-4A:
* 2
1 1 / 4
Stevenson-Arce:
* . 32
12 . 26 . 30 . 16
. 12 . 26 . 12 .
5 . 12 . 12 . 5 / 200
Atkinson:
* 1 1 / 8
1 1 1
1

6
src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs

@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Bmp, new BmpEncoder());
config.SetDecoder(ImageFormats.Bmp, new BmpDecoder());
config.AddImageFormatDetector(new BmpImageFormatDetector());
config.ImageFormatsManager.SetEncoder(ImageFormats.Bmp, new BmpEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Bmp, new BmpDecoder());
config.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector());
}
}
}

37
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -69,7 +69,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private BmpInfoHeader infoHeader;
private Configuration configuration;
private readonly Configuration configuration;
private readonly MemoryManager memoryManager;
/// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
@ -79,6 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
this.memoryManager = configuration.MemoryManager;
}
/// <summary>
@ -221,9 +224,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = Buffer2D<byte>.CreateClean(width, height))
using (var buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
{
this.UncompressRle8(width, buffer);
this.UncompressRle8(width, buffer.Span);
for (int y = 0; y < height; y++)
{
@ -343,15 +346,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
padding = 4 - padding;
}
using (var row = Buffer<byte>.CreateClean(arrayWidth + padding))
using (IManagedByteBuffer row = this.memoryManager.AllocateCleanManagedByteBuffer(arrayWidth + padding))
{
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
Span<byte> rowSpan = row.Span;
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
this.currentStream.Read(row.Array, 0, row.Length);
this.currentStream.Read(row.Array, 0, row.Length());
int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
@ -362,7 +367,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int shift = 0; shift < ppb && (x + shift) < width; shift++)
{
int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int newX = colOffset + shift;
// Stored in b-> g-> r order.
@ -393,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = new Buffer<byte>(stride))
using (var buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
{
@ -430,14 +435,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 3);
using (var row = new PixelArea<TPixel>(width, ComponentOrder.Zyx, padding))
using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 3, padding))
{
for (int y = 0; y < height; y++)
{
row.Read(this.currentStream);
this.currentStream.Read(row);
int newY = Invert(y, height, inverted);
pixels.CopyFrom(row, newY);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width);
}
}
}
@ -454,14 +460,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
using (var row = new PixelArea<TPixel>(width, ComponentOrder.Zyxw, padding))
using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 4, padding))
{
for (int y = 0; y < height; y++)
{
row.Read(this.currentStream);
this.currentStream.Read(row);
int newY = Invert(y, height, inverted);
pixels.CopyFrom(row, newY);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width);
}
}
}

3
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var encoder = new BmpEncoderCore(this);
var encoder = new BmpEncoderCore(this, image.GetMemoryManager());
encoder.Encode(image, stream);
}
}

28
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -4,6 +4,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@ -21,14 +22,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
private BmpBitsPerPixel bitsPerPixel;
private readonly BmpBitsPerPixel bitsPerPixel;
private readonly MemoryManager memoryManager;
/// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
/// </summary>
/// <param name="options">The encoder options</param>
public BmpEncoderCore(IBmpEncoderOptions options)
/// <param name="memoryManager">The memory manager</param>
public BmpEncoderCore(IBmpEncoderOptions options, MemoryManager memoryManager)
{
this.memoryManager = memoryManager;
this.bitsPerPixel = options.BitsPerPixel;
}
@ -145,6 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel)
{
return this.memoryManager.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
}
/// <summary>
/// Writes the 32bit color palette to the stream.
/// </summary>
@ -154,12 +164,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write32Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (PixelArea<TPixel> row = new PixelArea<TPixel>(pixels.Width, ComponentOrder.Zyxw, this.padding))
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
pixels.CopyTo(row, y);
writer.Write(row.Bytes, 0, row.Length);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length);
writer.Write(row.Array, 0, row.Length());
}
}
}
@ -173,12 +184,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write24Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (PixelArea<TPixel> row = new PixelArea<TPixel>(pixels.Width, ComponentOrder.Zyx, this.padding))
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
pixels.CopyTo(row, y);
writer.Write(row.Bytes, 0, row.Length);
Span<TPixel> pixelSpan = pixels.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgr24Bytes(pixelSpan, row.Span, pixelSpan.Length);
writer.Write(row.Array, 0, row.Length());
}
}
}

2
src/ImageSharp/Formats/Bmp/ImageExtensions.cs

@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream, BmpEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp));
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Bmp));
}
}

6
src/ImageSharp/Formats/Gif/GifConfigurationModule.cs

@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Gif, new GifEncoder());
config.SetDecoder(ImageFormats.Gif, new GifDecoder());
config.ImageFormatsManager.SetEncoder(ImageFormats.Gif, new GifEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Gif, new GifDecoder());
config.AddImageFormatDetector(new GifImageFormatDetector());
config.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector());
}
}
}

64
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The global color table.
/// </summary>
private Buffer<byte> globalColorTable;
private IManagedByteBuffer globalColorTable;
/// <summary>
/// The global color table length
@ -92,6 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public FrameDecodingMode DecodingMode { get; }
private MemoryManager MemoryManager => this.configuration.MemoryManager;
/// <summary>
/// Decodes the stream to the image.
/// </summary>
@ -333,18 +335,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
continue;
}
byte[] commentsBuffer = ArrayPool<byte>.Shared.Rent(length);
try
using (IManagedByteBuffer commentsBuffer = this.MemoryManager.AllocateManagedByteBuffer(length))
{
this.currentStream.Read(commentsBuffer, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer, 0, length);
this.currentStream.Read(commentsBuffer.Array, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length);
this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
finally
{
ArrayPool<byte>.Shared.Return(commentsBuffer);
}
}
}
@ -359,22 +355,23 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
Buffer<byte> localColorTable = null;
Buffer<byte> indices = null;
IManagedByteBuffer localColorTable = null;
IManagedByteBuffer indices = null;
try
{
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
if (imageDescriptor.LocalColorTableFlag)
{
int length = imageDescriptor.LocalColorTableSize * 3;
localColorTable = Buffer<byte>.CreateClean(length);
localColorTable = this.configuration.MemoryManager.AllocateManagedByteBuffer(length, true);
this.currentStream.Read(localColorTable.Array, 0, length);
}
indices = Buffer<byte>.CreateClean(imageDescriptor.Width * imageDescriptor.Height);
indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true);
this.ReadFrameIndices(imageDescriptor, indices);
this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor);
this.ReadFrameIndices(imageDescriptor, indices.Span);
IManagedByteBuffer colorTable = localColorTable ?? this.globalColorTable;
this.ReadFrameColors(ref image, ref previousFrame, indices.Span, colorTable.Span, imageDescriptor);
// Skip any remaining blocks
this.Skip(0);
@ -395,7 +392,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span<byte> indices)
{
int dataSize = this.currentStream.ReadByte();
using (var lzwDecoder = new LzwDecoder(this.currentStream))
using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryManager, this.currentStream))
{
lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
}
@ -445,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
imageFrame = currentFrame;
this.RestoreToBackground(imageFrame, image.Width, image.Height);
this.RestoreToBackground(imageFrame);
}
int i = 0;
@ -535,9 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The frame.</param>
/// <param name="imageWidth">Width of the image.</param>
/// <param name="imageHeight">Height of the image.</param>
private void RestoreToBackground<TPixel>(ImageFrame<TPixel> frame, int imageWidth, int imageHeight)
private void RestoreToBackground<TPixel>(ImageFrame<TPixel> frame)
where TPixel : struct, IPixel<TPixel>
{
if (this.restoreArea == null)
@ -545,28 +540,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
return;
}
// Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == imageWidth &&
this.restoreArea.Value.Height == imageHeight)
{
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{
pixelAccessor.Reset();
}
}
else
{
using (var emptyRow = new PixelArea<TPixel>(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
{
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{
for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++)
{
pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
}
}
}
}
BufferArea<TPixel> pixelArea = frame.PixelBuffer.GetArea(this.restoreArea.Value);
pixelArea.Clear();
this.restoreArea = null;
}
@ -606,7 +581,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = Buffer<byte>.CreateClean(this.globalColorTableLength);
this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(this.globalColorTableLength, true);
// Read the global color table from the stream
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);

4
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -5,6 +5,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers;
@ -44,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var encoder = new GifEncoderCore(this);
var encoder = new GifEncoderCore(image.GetConfiguration().MemoryManager, this);
encoder.Encode(image, stream);
}
}

26
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers;
@ -18,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
internal sealed class GifEncoderCore
{
private readonly MemoryManager memoryManager;
/// <summary>
/// The temp buffer used to reduce allocations.
/// </summary>
@ -61,9 +64,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="options">The options for the encoder.</param>
public GifEncoderCore(IGifEncoderOptions options)
public GifEncoderCore(MemoryManager memoryManager, IGifEncoderOptions options)
{
this.memoryManager = memoryManager;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
@ -350,24 +355,21 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Get max colors for bit depth.
int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;
byte[] colorTable = ArrayPool<byte>.Shared.Rent(colorTableLength);
var rgb = default(Rgb24);
try
using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
{
Span<byte> colorTableSpan = colorTable.Span;
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
image.Palette[i].ToRgb24(ref rgb);
colorTable[offset] = rgb.R;
colorTable[offset + 1] = rgb.G;
colorTable[offset + 2] = rgb.B;
colorTableSpan[offset] = rgb.R;
colorTableSpan[offset + 1] = rgb.G;
colorTableSpan[offset + 2] = rgb.B;
}
writer.Write(colorTable, 0, colorTableLength);
}
finally
{
ArrayPool<byte>.Shared.Return(colorTable);
writer.Write(colorTable.Array, 0, colorTableLength);
}
}
@ -380,7 +382,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteImageData<TPixel>(QuantizedImage<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth))
using (var encoder = new LzwEncoder(this.memoryManager, image.Pixels, (byte)this.bitDepth))
{
encoder.Encode(writer.BaseStream);
}

2
src/ImageSharp/Formats/Gif/ImageExtensions.cs

@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, GifEncoder encoder)
where TPixel : struct, IPixel<TPixel>
=> source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif));
=> source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Gif));
}
}

53
src/ImageSharp/Formats/Gif/LzwDecoder.cs

@ -5,6 +5,8 @@ using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
@ -30,17 +32,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The prefix buffer.
/// </summary>
private readonly int[] prefix;
private readonly IBuffer<int> prefix;
/// <summary>
/// The suffix buffer.
/// </summary>
private readonly int[] suffix;
private readonly IBuffer<int> suffix;
/// <summary>
/// The pixel stack buffer.
/// </summary>
private readonly int[] pixelStack;
private readonly IBuffer<int> pixelStack;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
@ -59,21 +61,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Initializes a new instance of the <see cref="LzwDecoder"/> class
/// and sets the stream, where the compressed data should be read from.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="stream">The stream to read from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="stream"/> is null.</exception>
public LzwDecoder(Stream stream)
public LzwDecoder(MemoryManager memoryManager, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.stream = stream;
this.prefix = ArrayPool<int>.Shared.Rent(MaxStackSize);
this.suffix = ArrayPool<int>.Shared.Rent(MaxStackSize);
this.pixelStack = ArrayPool<int>.Shared.Rent(MaxStackSize + 1);
Array.Clear(this.prefix, 0, MaxStackSize);
Array.Clear(this.suffix, 0, MaxStackSize);
Array.Clear(this.pixelStack, 0, MaxStackSize + 1);
this.prefix = memoryManager.Allocate<int>(MaxStackSize, true);
this.suffix = memoryManager.Allocate<int>(MaxStackSize, true);
this.pixelStack = memoryManager.Allocate<int>(MaxStackSize + 1, true);
}
/// <summary>
@ -116,10 +115,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
int data = 0;
int first = 0;
Span<int> prefixSpan = this.prefix.Span;
Span<int> suffixSpan = this.suffix.Span;
Span<int> pixelStackSpan = this.pixelStack.Span;
for (code = 0; code < clearCode; code++)
{
this.prefix[code] = 0;
this.suffix[code] = (byte)code;
prefixSpan[code] = 0;
suffixSpan[code] = (byte)code;
}
byte[] buffer = new byte[255];
@ -173,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (oldCode == NullCode)
{
this.pixelStack[top++] = this.suffix[code];
pixelStackSpan[top++] = suffixSpan[code];
oldCode = code;
first = code;
continue;
@ -182,27 +185,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
int inCode = code;
if (code == availableCode)
{
this.pixelStack[top++] = (byte)first;
pixelStackSpan[top++] = (byte)first;
code = oldCode;
}
while (code > clearCode)
{
this.pixelStack[top++] = this.suffix[code];
code = this.prefix[code];
pixelStackSpan[top++] = suffixSpan[code];
code = prefixSpan[code];
}
first = this.suffix[code];
first = suffixSpan[code];
this.pixelStack[top++] = this.suffix[code];
pixelStackSpan[top++] = suffixSpan[code];
// Fix for Gifs that have "deferred clear code" as per here :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if (availableCode < MaxStackSize)
{
this.prefix[availableCode] = oldCode;
this.suffix[availableCode] = first;
prefixSpan[availableCode] = oldCode;
suffixSpan[availableCode] = first;
availableCode++;
if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
{
@ -218,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--;
// Clear missing pixels
pixels[xyz++] = (byte)this.pixelStack[top];
pixels[xyz++] = (byte)pixelStackSpan[top];
}
}
@ -262,9 +265,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (disposing)
{
ArrayPool<int>.Shared.Return(this.prefix);
ArrayPool<int>.Shared.Return(this.suffix);
ArrayPool<int>.Shared.Return(this.pixelStack);
this.prefix?.Dispose();
this.suffix?.Dispose();
this.pixelStack?.Dispose();
}
this.isDisposed = true;

58
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -5,6 +5,8 @@ using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
@ -69,12 +71,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// The hash table.
/// </summary>
private readonly int[] hashTable;
private readonly IBuffer<int> hashTable;
/// <summary>
/// The code table.
/// </summary>
private readonly int[] codeTable;
private readonly IBuffer<int> codeTable;
/// <summary>
/// Define the storage for the packet accumulator.
@ -189,17 +191,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Initializes a new instance of the <see cref="LzwEncoder"/> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="indexedPixels">The array of indexed pixels.</param>
/// <param name="colorDepth">The color depth in bits.</param>
public LzwEncoder(byte[] indexedPixels, int colorDepth)
public LzwEncoder(MemoryManager memoryManager, byte[] indexedPixels, int colorDepth)
{
this.pixelArray = indexedPixels;
this.initialCodeSize = Math.Max(2, colorDepth);
this.hashTable = ArrayPool<int>.Shared.Rent(HashSize);
this.codeTable = ArrayPool<int>.Shared.Rent(HashSize);
Array.Clear(this.hashTable, 0, HashSize);
Array.Clear(this.codeTable, 0, HashSize);
this.hashTable = memoryManager.Allocate<int>(HashSize, true);
this.codeTable = memoryManager.Allocate<int>(HashSize, true);
}
/// <summary>
@ -258,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="stream">The output stream.</param>
private void ClearBlock(Stream stream)
{
this.ResetCodeTable(this.hsize);
this.ResetCodeTable();
this.freeEntry = this.clearCode + 2;
this.clearFlag = true;
@ -268,13 +269,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reset the code table.
/// </summary>
/// <param name="size">The hash size.</param>
private void ResetCodeTable(int size)
private void ResetCodeTable()
{
for (int i = 0; i < size; ++i)
{
this.hashTable[i] = -1;
}
this.hashTable.Span.Fill(-1);
// Original code:
// for (int i = 0; i < size; ++i)
// {
// this.hashTable[i] = -1;
// }
}
/// <summary>
@ -316,23 +319,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
hsizeReg = this.hsize;
this.ResetCodeTable(hsizeReg); // clear hash table
this.ResetCodeTable(); // clear hash table
this.Output(this.clearCode, stream);
Span<int> hashTableSpan = this.hashTable.Span;
Span<int> codeTableSpan = this.codeTable.Span;
while ((c = this.NextPixel()) != Eof)
{
fcode = (c << this.maxbits) + ent;
int i = (c << hshift) ^ ent /* = 0 */;
if (this.hashTable[i] == fcode)
if (hashTableSpan[i] == fcode)
{
ent = this.codeTable[i];
ent = codeTableSpan[i];
continue;
}
// Non-empty slot
if (this.hashTable[i] >= 0)
if (hashTableSpan[i] >= 0)
{
int disp = hsizeReg - i;
if (i == 0)
@ -347,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
i += hsizeReg;
}
if (this.hashTable[i] == fcode)
if (hashTableSpan[i] == fcode)
{
ent = this.codeTable[i];
ent = codeTableSpan[i];
break;
}
}
while (this.hashTable[i] >= 0);
while (hashTableSpan[i] >= 0);
if (this.hashTable[i] == fcode)
if (hashTableSpan[i] == fcode)
{
continue;
}
@ -365,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = c;
if (this.freeEntry < this.maxmaxcode)
{
this.codeTable[i] = this.freeEntry++; // code -> hashtable
this.hashTable[i] = fcode;
codeTableSpan[i] = this.freeEntry++; // code -> hashtable
hashTableSpan[i] = fcode;
}
else
{
@ -483,8 +489,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (disposing)
{
ArrayPool<int>.Shared.Return(this.hashTable);
ArrayPool<int>.Shared.Return(this.codeTable);
this.hashTable?.Dispose();
this.codeTable?.Dispose();
}
this.isDisposed = true;

2
src/ImageSharp/Formats/Gif/PackedField.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Represents a byte of data in a GIF data stream which contains a number
/// of data items.
/// </summary>
internal struct PackedField : IEquatable<PackedField>
internal readonly struct PackedField : IEquatable<PackedField>
{
/// <summary>
/// The individual bits representing the packed byte.

186
src/ImageSharp/Formats/ImageFormatManager.cs

@ -0,0 +1,186 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Collection of Image Formats to be used in <see cref="Configuration" /> class.
/// </summary>
public class ImageFormatManager
{
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly ConcurrentBag<IImageFormat> imageFormats = new ConcurrentBag<IImageFormat>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatManager" /> class.
/// </summary>
public ImageFormatManager()
{
}
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
this.imageFormats.Add(format);
}
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtension(string extension)
{
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary>
/// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType)
{
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Sets a specific image encoder as the encoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
}
/// <summary>
/// Sets a specific image decoder as the decoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Removes all the registered image format detectors.
/// </summary>
public void ClearImageFormatDetectors()
{
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
}
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector)
{
Guard.NotNull(detector, nameof(detector));
this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
return decoder;
}
return null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
return null;
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}

5
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public Block8x8(Span<short> coefficients)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte sourceRef = ref coefficients.NonPortableCast<short, byte>().DangerousGetPinnableReference();
ref byte sourceRef = ref MemoryMarshal.GetReference(coefficients.NonPortableCast<short, byte>());
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
@ -204,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void CopyTo(Span<short> destination)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte destRef = ref destination.NonPortableCast<short, byte>().DangerousGetPinnableReference();
ref byte destRef = ref MemoryMarshal.GetReference(destination.NonPortableCast<short, byte>());
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}

11
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs

@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return;
}
ref float destBase = ref area.GetReferenceToOrigin();
// TODO: Optimize: implement all the cases with loopless special code! (T4?)
for (int y = 0; y < 8; y++)
{
@ -40,9 +42,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * area.Stride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
area[xx + j, yy + i] = value;
// area[xx + j, yy + i] = value;
Unsafe.Add(ref destBase, baseIdx + j) = value;
}
}
}
@ -53,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void CopyTo(BufferArea<float> area)
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigo());
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigin());
int destStride = area.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
@ -76,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
private void CopyTo2x2(BufferArea<float> area)
{
ref float destBase = ref area.GetReferenceToOrigo();
ref float destBase = ref area.GetReferenceToOrigin();
int destStride = area.Stride;
this.WidenCopyImpl2x2(ref destBase, 0, destStride);

4
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void LoadFrom(Span<float> source)
{
ref byte s = ref Unsafe.As<float, byte>(ref source.DangerousGetPinnableReference());
ref byte s = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(source));
ref byte d = ref Unsafe.As<Block8x8F, byte>(ref this);
Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(Span<float> dest)
{
ref byte d = ref Unsafe.As<float, byte>(ref dest.DangerousGetPinnableReference());
ref byte d = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(dest));
ref byte s = ref Unsafe.As<Block8x8F, byte>(ref this);
Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));

2
src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// Provides information about the Adobe marker segment.
/// </summary>
/// <remarks>See the included 5116.DCT.pdf file in the source for more information.</remarks>
internal struct AdobeMarker : IEquatable<AdobeMarker>
internal readonly struct AdobeMarker : IEquatable<AdobeMarker>
{
/// <summary>
/// Gets the length of an adobe marker segment.

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
@ -37,14 +38,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!");
ref Vector4Pair yBase =
ref Unsafe.As<float, Vector4Pair>(ref values.Component0.DangerousGetPinnableReference());
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector4Pair cbBase =
ref Unsafe.As<float, Vector4Pair>(ref values.Component1.DangerousGetPinnableReference());
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector4Pair crBase =
ref Unsafe.As<float, Vector4Pair>(ref values.Component2.DangerousGetPinnableReference());
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref result.DangerousGetPinnableReference());
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector4(-128f);

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
@ -46,14 +47,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
}
ref Vector<float> yBase =
ref Unsafe.As<float, Vector<float>>(ref values.Component0.DangerousGetPinnableReference());
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector<float> cbBase =
ref Unsafe.As<float, Vector<float>>(ref values.Component1.DangerousGetPinnableReference());
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector<float> crBase =
ref Unsafe.As<float, Vector<float>>(ref values.Component2.DangerousGetPinnableReference());
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref result.DangerousGetPinnableReference());
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector<float>(-128f);

4
src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs

@ -66,7 +66,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
/// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.
/// </summary>
public struct ComponentValues
#pragma warning disable SA1206 // Declaration keywords should follow order
public readonly ref struct ComponentValues
#pragma warning restore SA1206 // Declaration keywords should follow order
{
/// <summary>
/// The component count

2
src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// Provides information about the JFIF marker segment
/// TODO: Thumbnail?
/// </summary>
internal struct JFifMarker : IEquatable<JFifMarker>
internal readonly struct JFifMarker : IEquatable<JFifMarker>
{
/// <summary>
/// Gets the length of an JFIF marker segment.

2
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs

@ -8,7 +8,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
/// <summary>
/// Encapsulates the implementation of processing "raw" <see cref="Buffer{T}"/>-s into Jpeg image channels.
/// Encapsulates the implementation of processing "raw" <see cref="IBuffer{T}"/>-s into Jpeg image channels.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct JpegBlockPostProcessor

8
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs

@ -22,11 +22,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <summary>
/// Initializes a new instance of the <see cref="JpegComponentPostProcessor"/> class.
/// </summary>
public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
public JpegComponentPostProcessor(MemoryManager memoryManager, JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
{
this.Component = component;
this.ImagePostProcessor = imagePostProcessor;
this.ColorBuffer = new Buffer2D<float>(imagePostProcessor.PostProcessorBufferSize);
this.ColorBuffer = memoryManager.Allocate2D<float>(
imagePostProcessor.PostProcessorBufferSize.Width,
imagePostProcessor.PostProcessorBufferSize.Height);
this.BlockRowsPerStep = JpegImagePostProcessor.BlockRowsPerStep / this.Component.SubSamplingDivisors.Height;
this.blockAreaSize = this.Component.SubSamplingDivisors * 8;
@ -43,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
public IJpegComponent Component { get; }
/// <summary>
/// Gets the temporal working buffer of color values.
/// Gets the temporary working buffer of color values.
/// </summary>
public Buffer2D<float> ColorBuffer { get; }

15
src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <summary>
/// Temporal buffer to store a row of colors.
/// </summary>
private readonly Buffer<Vector4> rgbaBuffer;
private readonly IBuffer<Vector4> rgbaBuffer;
/// <summary>
/// The <see cref="ColorConverters.JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.
@ -44,16 +44,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <summary>
/// Initializes a new instance of the <see cref="JpegImagePostProcessor"/> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="rawJpeg">The <see cref="IRawJpegData"/> representing the uncompressed spectral Jpeg data</param>
public JpegImagePostProcessor(IRawJpegData rawJpeg)
public JpegImagePostProcessor(MemoryManager memoryManager, IRawJpegData rawJpeg)
{
this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First();
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray();
this.rgbaBuffer = new Buffer<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryManager, this, c)).ToArray();
this.rgbaBuffer = memoryManager.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
}
@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
public int NumberOfPostProcessorSteps { get; }
/// <summary>
/// Gets the size of the temporal buffers we need to allocate into <see cref="JpegComponentPostProcessor.ColorBuffer"/>.
/// Gets the size of the temporary buffers we need to allocate into <see cref="JpegComponentPostProcessor.ColorBuffer"/>.
/// </summary>
public Size PostProcessorBufferSize { get; }
@ -149,11 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
int y = yy - this.PixelRowCounter;
var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer);
this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer.Span);
Span<TPixel> destRow = destination.GetPixelRowSpan(yy);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer, destRow, destination.Width);
PixelOperations<TPixel>.Instance.PackFromVector4(this.rgbaBuffer.Span, destRow, destination.Width);
}
}
}

26
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
// <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
internal unsafe partial struct GenericBlock8x8<T>
{
#pragma warning disable 169
// It's not allowed use fix-sized buffers with generics, need to place all the fields manually:
private T _y0_x0, _y0_x1, _y0_x2, _y0_x3, _y0_x4, _y0_x5, _y0_x6, _y0_x7;
private T _y1_x0, _y1_x1, _y1_x2, _y1_x3, _y1_x4, _y1_x5, _y1_x6, _y1_x7;
private T _y2_x0, _y2_x1, _y2_x2, _y2_x3, _y2_x4, _y2_x5, _y2_x6, _y2_x7;
private T _y3_x0, _y3_x1, _y3_x2, _y3_x3, _y3_x4, _y3_x5, _y3_x6, _y3_x7;
private T _y4_x0, _y4_x1, _y4_x2, _y4_x3, _y4_x4, _y4_x5, _y4_x6, _y4_x7;
private T _y5_x0, _y5_x1, _y5_x2, _y5_x3, _y5_x4, _y5_x5, _y5_x6, _y5_x7;
private T _y6_x0, _y6_x1, _y6_x2, _y6_x3, _y6_x4, _y6_x5, _y6_x6, _y6_x7;
private T _y7_x0, _y7_x1, _y7_x2, _y7_x3, _y7_x4, _y7_x5, _y7_x6, _y7_x7;
#pragma warning restore 169
}
}

43
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt

@ -0,0 +1,43 @@
<#
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
#>
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
// <auto-generated />
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
internal unsafe partial struct GenericBlock8x8<T>
{
#pragma warning disable 169
// It's not allowed use fix-sized buffers with generics, need to place all the fields manually:
<#
PushIndent(" ");
Write(" ");
for (int y = 0; y < 8; y++)
{
Write("private T ");
for (int x = 0; x < 8; x++)
{
Write($"_y{y}_x{x}");
if (x < 7) Write(", ");
}
WriteLine(";");
}
PopIndent();
#>
#pragma warning restore 169
}
}

129
src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs

@ -0,0 +1,129 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
/// <summary>
/// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct GenericBlock8x8<T>
where T : struct
{
public const int Size = 64;
public const int SizeInBytes = Size * 3;
/// <summary>
/// FOR TESTING ONLY!
/// Gets or sets a <see cref="Rgb24"/> value at the given index
/// </summary>
/// <param name="idx">The index</param>
/// <returns>The value</returns>
public T this[int idx]
{
get
{
ref T selfRef = ref Unsafe.As<GenericBlock8x8<T>, T>(ref this);
return Unsafe.Add(ref selfRef, idx);
}
set
{
ref T selfRef = ref Unsafe.As<GenericBlock8x8<T>, T>(ref this);
Unsafe.Add(ref selfRef, idx) = value;
}
}
/// <summary>
/// FOR TESTING ONLY!
/// Gets or sets a value in a row+coulumn of the 8x8 block
/// </summary>
/// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param>
/// <returns>The value</returns>
public T this[int x, int y]
{
get => this[(y * 8) + x];
set => this[(y * 8) + x] = value;
}
public void LoadAndStretchEdges<TPixel>(IPixelSource<TPixel> source, int sourceX, int sourceY)
where TPixel : struct, IPixel<TPixel>
{
var buffer = source.PixelBuffer as Buffer2D<T>;
if (buffer == null)
{
throw new InvalidOperationException("LoadAndStretchEdges<TPixels>() is only valid for TPixel == T !");
}
this.LoadAndStretchEdges(buffer, sourceX, sourceY);
}
/// <summary>
/// Load a 8x8 region of an image into the block.
/// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image.
/// </summary>
public void LoadAndStretchEdges(Buffer2D<T> source, int sourceX, int sourceY)
{
int width = Math.Min(8, source.Width - sourceX);
int height = Math.Min(8, source.Height - sourceY);
if (width <= 0 || height <= 0)
{
return;
}
uint byteWidth = (uint)width * (uint)Unsafe.SizeOf<T>();
int remainderXCount = 8 - width;
ref byte blockStart = ref Unsafe.As<GenericBlock8x8<T>, byte>(ref this);
ref byte imageStart = ref Unsafe.As<T, byte>(
ref Unsafe.Add(ref MemoryMarshal.GetReference(source.GetRowSpan(sourceY)), sourceX));
int blockRowSizeInBytes = 8 * Unsafe.SizeOf<T>();
int imageRowSizeInBytes = source.Width * Unsafe.SizeOf<T>();
for (int y = 0; y < height; y++)
{
ref byte s = ref Unsafe.Add(ref imageStart, y * imageRowSizeInBytes);
ref byte d = ref Unsafe.Add(ref blockStart, y * blockRowSizeInBytes);
Unsafe.CopyBlock(ref d, ref s, byteWidth);
ref T last = ref Unsafe.Add(ref Unsafe.As<byte, T>(ref d), width - 1);
for (int x = 1; x <= remainderXCount; x++)
{
Unsafe.Add(ref last, x) = last;
}
}
int remainderYCount = 8 - height;
if (remainderYCount == 0)
{
return;
}
ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * blockRowSizeInBytes);
for (int y = 1; y <= remainderYCount; y++)
{
ref byte remStart = ref Unsafe.Add(ref lastRowStart, blockRowSizeInBytes * y);
Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)blockRowSizeInBytes);
}
}
/// <summary>
/// Only for on-stack instances!
/// </summary>
public Span<T> AsSpanUnsafe() => new Span<T>(Unsafe.AsPointer(ref this), Size);
}
}

18
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
@ -12,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// Bytes is a byte buffer, similar to a stream, except that it
/// has to be able to unread more than 1 byte, due to byte stuffing.
/// Byte stuffing is specified in section F.1.2.3.
/// TODO: Optimize buffer management inside this class!
/// </summary>
internal struct Bytes : IDisposable
{
@ -48,20 +48,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
public int UnreadableBytes;
private static readonly ArrayPool<byte> BytePool = ArrayPool<byte>.Create(BufferSize, 50);
private static readonly ArrayPool<int> IntPool = ArrayPool<int>.Create(BufferSize, 50);
/// <summary>
/// Creates a new instance of the <see cref="Bytes"/>, and initializes it's buffer.
/// </summary>
/// <returns>The bytes created</returns>
public static Bytes Create()
{
// DO NOT bother with buffers and array pooling here!
// It only makes things worse!
return new Bytes
{
Buffer = BytePool.Rent(BufferSize),
BufferAsInt = IntPool.Rent(BufferSize)
Buffer = new byte[BufferSize],
BufferAsInt = new int[BufferSize]
};
}
@ -70,12 +68,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
/// </summary>
public void Dispose()
{
if (this.Buffer != null)
{
BytePool.Return(this.Buffer);
IntPool.Return(this.BufferAsInt);
}
this.Buffer = null;
this.BufferAsInt = null;
}

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

Loading…
Cancel
Save