Browse Source

Merge branch 'master' into feature/memory-manager

# Conflicts:
#	src/ImageSharp/Formats/Gif/GifDecoderCore.cs
#	src/ImageSharp/Formats/Png/PngEncoderCore.cs
#	src/ImageSharp/Image/ImageFrame{TPixel}.cs
#	src/ImageSharp/Memory/Buffer{T}.cs
#	src/ImageSharp/Memory/PixelDataPool{T}.cs
#	src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
#	src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
#	src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
#	src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
#	src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
#	src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
#	tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs
#	tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs
af/merge-core
Anton Firszov 8 years ago
parent
commit
7b183df73d
  1. 3
      .gitignore
  2. 13
      APACHE-2.0-LICENSE.txt
  3. 14
      ImageSharp.v2.ncrunchsolution
  4. 6
      ImageSharp.v3.ncrunchsolution
  5. 201
      LICENSE
  6. 1
      NuGet.config
  7. 4
      config.wyam
  8. 153
      packages.xml
  9. 6
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  10. 14
      src/ImageSharp/Common/Extensions/ComparableExtensions.cs
  11. 28
      src/ImageSharp/Common/Extensions/Vector4Extensions.cs
  12. 21
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  13. 4
      src/ImageSharp/DefaultInternalImageProcessorContext.cs
  14. 8
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
  15. 12
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  16. 134
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  17. 17
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  18. 176
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  19. 5
      src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
  20. 2
      src/ImageSharp/Formats/IImageDecoder.cs
  21. 21
      src/ImageSharp/Formats/IImageInfoDetector.cs
  22. 13
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
  23. 25
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  24. 14
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  25. 25
      src/ImageSharp/Formats/PixelTypeInfo.cs
  26. 11
      src/ImageSharp/Formats/Png/PngDecoder.cs
  27. 105
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  28. 2
      src/ImageSharp/Formats/Png/PngEncoder.cs
  29. 7
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  30. 12
      src/ImageSharp/Image/IImage.cs
  31. 35
      src/ImageSharp/Image/IImageInfo.cs
  32. 14
      src/ImageSharp/Image/Image.Decode.cs
  33. 31
      src/ImageSharp/Image/Image.FromStream.cs
  34. 16
      src/ImageSharp/Image/ImageFrame{TPixel}.cs
  35. 41
      src/ImageSharp/Image/ImageInfo.cs
  36. 21
      src/ImageSharp/Image/Image{TPixel}.cs
  37. 1
      src/ImageSharp/ImageSharp.csproj
  38. 12
      src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs
  39. 6
      src/ImageSharp/PixelFormats/Rgb24.cs
  40. 2
      src/ImageSharp/PixelFormats/Rgba32.cs
  41. 4
      src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs
  42. 16
      src/ImageSharp/Processing/ColorMatrix/Brightness.cs
  43. 19
      src/ImageSharp/Processing/ColorMatrix/Contrast.cs
  44. 21
      src/ImageSharp/Processing/ColorMatrix/Filter.cs
  45. 72
      src/ImageSharp/Processing/ColorMatrix/Grayscale.cs
  46. 6
      src/ImageSharp/Processing/ColorMatrix/Hue.cs
  47. 446
      src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs
  48. 14
      src/ImageSharp/Processing/ColorMatrix/Opacity.cs
  49. 54
      src/ImageSharp/Processing/ColorMatrix/Saturate.cs
  50. 31
      src/ImageSharp/Processing/ColorMatrix/Sepia.cs
  51. 4
      src/ImageSharp/Processing/Effects/Invert.cs
  52. 2
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
  53. 2
      src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
  54. 2
      src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
  55. 6
      src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
  56. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs
  57. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
  58. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
  59. 31
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
  60. 78
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
  61. 32
      src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs
  62. 32
      src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs
  63. 77
      src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs
  64. 27
      src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs
  65. 28
      src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs
  66. 66
      src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs
  67. 35
      src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs
  68. 4
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  69. 4
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  70. 4
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
  71. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
  72. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  73. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
  74. 81
      src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs
  75. 87
      src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs
  76. 89
      src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs
  77. 66
      src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs
  78. 23
      src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs
  79. 34
      src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs
  80. 23
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs
  81. 23
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs
  82. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs
  83. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs
  84. 23
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs
  85. 21
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs
  86. 0
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md
  87. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs
  88. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs
  89. 34
      src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs
  90. 62
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
  91. 30
      src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs
  92. 30
      src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs
  93. 29
      src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs
  94. 30
      src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs
  95. 23
      src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs
  96. 15
      src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
  97. 30
      src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs
  98. 24
      src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
  99. 34
      src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs
  100. 30
      src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs

3
.gitignore

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

13
APACHE-2.0-LICENSE.txt

@ -1,13 +0,0 @@
Copyright 2012 James South
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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>

201
LICENSE

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Six Labors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1
NuGet.config

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<packageSources> <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="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://api.nuget.org/v3/index.json" />
<add key="nuget.org" value="https://www.nuget.org/api/v2/" /> <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>

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

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

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

@ -169,19 +169,5 @@ namespace SixLabors.ImageSharp
{ {
return (byte)value.Clamp(0, 255); 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;
}
} }
} }

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

@ -12,6 +12,34 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
internal static class Vector4Extensions 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> /// <summary>
/// Compresses a linear color signal to its sRGB equivalent. /// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/> /// <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); 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> /// <summary>
/// Finds the bounding rectangle based on the first instance of any color component other /// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one. /// than the given one.

4
src/ImageSharp/DefaultInternalImageProcessorContext.cs

@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp
if (!this.mutate && this.destination == null) if (!this.mutate && this.destination == null)
{ {
// This will only work if the first processor applied is the cloning one thus // 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 // realistically for this optimization to work the resize must the first processor
// applied any only up processors will take the douple data path. // applied any only up processors will take the double data path.
if (processor is ICloningImageProcessor<TPixel> cloningImageProcessor) if (processor is ICloningImageProcessor<TPixel> cloningImageProcessor)
{ {
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle);

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

@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
this.startingOffset = 0; this.startingOffset = 0;
for (int i = 0; i < this.matrixWidth; i++) 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 // ReSharper disable once CompareOfFloatsByEqualityOperator
if (matrix[0, i] != 0) if (matrix[0, i] != 0)
{ {
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
// Calculate the error // Calculate the error
Vector4 error = source.ToVector4() - transformed.ToVector4(); 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++) for (int row = 0; row < this.matrixHeight; row++)
{ {
int matrixY = y + row; int matrixY = y + row;
@ -115,10 +115,8 @@ namespace SixLabors.ImageSharp.Dithering.Base
ref TPixel pixel = ref rowSpan[matrixX]; ref TPixel pixel = ref rowSpan[matrixX];
var offsetColor = pixel.ToVector4(); var offsetColor = pixel.ToVector4();
var coefficientVector = new Vector4(coefficient);
Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor;
result.W = offsetColor.W;
pixel.PackFromVector4(result); pixel.PackFromVector4(result);
} }
} }

12
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -23,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Formats will be supported in a later releases. We advise always /// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps. /// to use only 24 Bit Windows bitmaps.
/// </remarks> /// </remarks>
public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector
{ {
/// <inheritdoc/> /// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream) public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
@ -34,5 +32,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream); return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
} }
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
return new BmpDecoderCore(configuration, this).Identify(stream);
}
} }
} }

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

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp namespace SixLabors.ImageSharp.Formats.Bmp
@ -94,62 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Image<TPixel> Decode<TPixel>(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
this.currentStream = stream;
try try
{ {
this.ReadFileHeader(); this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
// is the upper-left corner and not the lower-left.The inverted flag
// indicates a lower-left origin.Our code will be outputting an
// upper-left origin pixel array.
bool inverted = false;
if (this.infoHeader.Height < 0)
{
inverted = true;
this.infoHeader.Height = -this.infoHeader.Height;
}
int colorMapSize = -1;
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
}
byte[] palette = null;
if (colorMapSize > 0)
{
// 256 * 4
if (colorMapSize > 1024)
{
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height); var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TPixel> pixels = image.Lock()) using (PixelAccessor<TPixel> pixels = image.Lock())
@ -192,6 +140,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
} }
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream)
{
this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData());
}
/// <summary> /// <summary>
/// Returns the y- value based on the given height. /// Returns the y- value based on the given height.
/// </summary> /// </summary>
@ -624,5 +582,73 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Offset = BitConverter.ToInt32(data, 10) Offset = BitConverter.ToInt32(data, 10)
}; };
} }
/// <summary>
/// Reads the <see cref="BmpFileHeader"/> and <see cref="BmpInfoHeader"/> from the stream and sets the corresponding fields.
/// </summary>
private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
this.currentStream = stream;
try
{
this.ReadFileHeader();
this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
// If the height is negative, then this is a Windows bitmap whose origin
// is the upper-left corner and not the lower-left.The inverted flag
// indicates a lower-left origin.Our code will be outputting an
// upper-left origin pixel array.
inverted = false;
if (this.infoHeader.Height < 0)
{
inverted = true;
this.infoHeader.Height = -this.infoHeader.Height;
}
int colorMapSize = -1;
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
}
palette = null;
if (colorMapSize > 0)
{
// 256 * 4
if (colorMapSize > 1024)
{
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
}
catch (IndexOutOfRangeException e)
{
throw new ImageFormatException("Bitmap does not have a valid format.", e);
}
}
} }
} }

17
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Decoder for generating an image out of a gif encoded stream. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </summary>
public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -33,8 +31,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream) public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
var decoder = new GifDecoderCore<TPixel>(configuration, this); var decoder = new GifDecoderCore(configuration, this);
return decoder.Decode(stream); return decoder.Decode<TPixel>(stream);
}
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
var decoder = new GifDecoderCore(configuration, this);
return decoder.Identify(stream);
} }
} }
} }

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

@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> internal sealed class GifDecoderCore
internal sealed class GifDecoderCore<TPixel>
where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// The temp buffer used to reduce allocations. /// The temp buffer used to reduce allocations.
@ -46,11 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary> /// </summary>
private int globalColorTableLength; private int globalColorTableLength;
/// <summary>
/// The previous frame.
/// </summary>
private ImageFrame<TPixel> previousFrame;
/// <summary> /// <summary>
/// The area to restore. /// The area to restore.
/// </summary> /// </summary>
@ -72,12 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private ImageMetaData metaData; private ImageMetaData metaData;
/// <summary> /// <summary>
/// The image to decode the information to. /// Initializes a new instance of the <see cref="GifDecoderCore"/> class.
/// </summary>
private Image<TPixel> image;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="options">The decoder options.</param> /// <param name="options">The decoder options.</param>
@ -107,28 +95,85 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream containing image data. </param> /// <param name="stream">The stream containing image data. </param>
/// <returns>The decoded image</returns> /// <returns>The decoded image</returns>
public Image<TPixel> Decode(Stream stream) public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel>
{ {
Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null;
try try
{ {
this.metaData = new ImageMetaData(); this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
this.currentStream = stream; // Loop though the respective gif parts and read the data.
int nextFlag = stream.ReadByte();
while (nextFlag != GifConstants.Terminator)
{
if (nextFlag == GifConstants.ImageLabel)
{
if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First)
{
break;
}
// Skip the identifier this.ReadFrame(ref image, ref previousFrame);
this.currentStream.Skip(6); }
this.ReadLogicalScreenDescriptor(); else if (nextFlag == GifConstants.ExtensionIntroducer)
{
int label = stream.ReadByte();
switch (label)
{
case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
break;
case GifConstants.CommentLabel:
this.ReadComments();
break;
case GifConstants.ApplicationExtensionLabel:
if (this.logicalScreenDescriptor.GlobalColorTableFlag) // The application extension length should be 11 but we've got test images that incorrectly
{ // set this to 252.
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; int appLength = stream.ReadByte();
this.Skip(appLength); // No need to read.
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
break;
}
}
else if (nextFlag == GifConstants.EndIntroducer)
{
break;
}
this.globalColorTable = this.configuration.MemoryManager.Allocate<byte>(this.globalColorTableLength, true); this.globalColorTable = this.configuration.MemoryManager.Allocate<byte>(this.globalColorTableLength, true);
// Read the global color table from the stream nextFlag = stream.ReadByte();
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); if (nextFlag == -1)
{
break;
}
} }
}
finally
{
this.globalColorTable?.Dispose();
}
return image;
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream)
{
try
{
this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
// Loop though the respective gif parts and read the data. // Loop though the respective gif parts and read the data.
int nextFlag = stream.ReadByte(); int nextFlag = stream.ReadByte();
@ -136,12 +181,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
if (nextFlag == GifConstants.ImageLabel) if (nextFlag == GifConstants.ImageLabel)
{ {
if (this.previousFrame != null && this.DecodingMode == FrameDecodingMode.First) // Skip image block
{ this.Skip(0);
break;
}
this.ReadFrame();
} }
else if (nextFlag == GifConstants.ExtensionIntroducer) else if (nextFlag == GifConstants.ExtensionIntroducer)
{ {
@ -149,7 +190,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (label) switch (label)
{ {
case GifConstants.GraphicControlLabel: case GifConstants.GraphicControlLabel:
this.ReadGraphicalControlExtension();
// Skip graphic control extension block
this.Skip(0);
break; break;
case GifConstants.CommentLabel: case GifConstants.CommentLabel:
this.ReadComments(); this.ReadComments();
@ -184,7 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.globalColorTable?.Dispose(); this.globalColorTable?.Dispose();
} }
return this.image; return new ImageInfo(new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, this.metaData);
} }
/// <summary> /// <summary>
@ -242,6 +285,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
Width = BitConverter.ToInt16(this.buffer, 0), Width = BitConverter.ToInt16(this.buffer, 0),
Height = BitConverter.ToInt16(this.buffer, 2), Height = BitConverter.ToInt16(this.buffer, 2),
BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1
BackgroundColorIndex = this.buffer[5], BackgroundColorIndex = this.buffer[5],
PixelAspectRatio = this.buffer[6], PixelAspectRatio = this.buffer[6],
GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1, GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
@ -302,7 +346,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Reads an individual gif frame. /// Reads an individual gif frame.
/// </summary> /// </summary>
private void ReadFrame() /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</param>
private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame)
where TPixel : struct, IPixel<TPixel>
{ {
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
@ -321,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
indices = this.configuration.MemoryManager.Allocate<byte>(imageDescriptor.Width * imageDescriptor.Height, true); indices = this.configuration.MemoryManager.Allocate<byte>(imageDescriptor.Width * imageDescriptor.Height, true);
this.ReadFrameIndices(imageDescriptor, indices); this.ReadFrameIndices(imageDescriptor, indices);
this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor); this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor);
// Skip any remaining blocks // Skip any remaining blocks
this.Skip(0); this.Skip(0);
@ -351,10 +399,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Reads the frames colors, mapping indices to colors. /// Reads the frames colors, mapping indices to colors.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to decode the information to.</param>
/// <param name="previousFrame">The previous frame.</param>
/// <param name="indices">The indexed pixels.</param> /// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors(Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor) private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor)
where TPixel : struct, IPixel<TPixel>
{ {
int imageWidth = this.logicalScreenDescriptor.Width; int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height; int imageHeight = this.logicalScreenDescriptor.Height;
@ -365,30 +417,30 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageFrame<TPixel> imageFrame; ImageFrame<TPixel> imageFrame;
if (this.previousFrame == null) if (previousFrame == null)
{ {
// This initializes the image to become fully transparent because the alpha channel is zero. // This initializes the image to become fully transparent because the alpha channel is zero.
this.image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, this.metaData); image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, this.metaData);
this.SetFrameMetaData(this.image.Frames.RootFrame.MetaData); this.SetFrameMetaData(image.Frames.RootFrame.MetaData);
imageFrame = this.image.Frames.RootFrame; imageFrame = image.Frames.RootFrame;
} }
else else
{ {
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious) this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{ {
prevFrame = this.previousFrame; prevFrame = previousFrame;
} }
currentFrame = this.image.Frames.AddFrame(this.previousFrame); // This clones the frame and adds it the collection currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection
this.SetFrameMetaData(currentFrame.MetaData); this.SetFrameMetaData(currentFrame.MetaData);
imageFrame = currentFrame; imageFrame = currentFrame;
this.RestoreToBackground(imageFrame); this.RestoreToBackground(imageFrame, image.Width, image.Height);
} }
int i = 0; int i = 0;
@ -460,11 +512,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (prevFrame != null) if (prevFrame != null)
{ {
this.previousFrame = prevFrame; previousFrame = prevFrame;
return; return;
} }
this.previousFrame = currentFrame ?? this.image.Frames.RootFrame; previousFrame = currentFrame ?? image.Frames.RootFrame;
if (this.graphicsControlExtension != null && if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -476,8 +528,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Restores the current frame area to the background. /// Restores the current frame area to the background.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The frame.</param> /// <param name="frame">The frame.</param>
private void RestoreToBackground(ImageFrame<TPixel> frame) /// <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)
where TPixel : struct, IPixel<TPixel>
{ {
if (this.restoreArea == null) if (this.restoreArea == null)
{ {
@ -485,8 +541,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
// Optimization for when the size of the frame is the same as the image size. // Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.image.Width && if (this.restoreArea.Value.Width == imageWidth &&
this.restoreArea.Value.Height == this.image.Height) this.restoreArea.Value.Height == imageHeight)
{ {
using (PixelAccessor<TPixel> pixelAccessor = frame.Lock()) using (PixelAccessor<TPixel> pixelAccessor = frame.Lock())
{ {
@ -527,5 +583,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod; meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
} }
} }
/// <summary>
/// Reads the logical screen descriptor and global color table blocks
/// </summary>
/// <param name="stream">The stream containing image data. </param>
private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
{
this.metaData = new ImageMetaData();
this.currentStream = stream;
// Skip the identifier
this.currentStream.Skip(6);
this.ReadLogicalScreenDescriptor();
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = Buffer<byte>.CreateClean(this.globalColorTableLength);
// Read the global color table from the stream
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
}
}
} }
} }

5
src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs

@ -22,6 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary> /// </summary>
public short Height { get; set; } public short Height { get; set; }
/// <summary>
/// Gets or sets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel { get; set; }
/// <summary> /// <summary>
/// Gets or sets the index at the Global Color Table for the Background Color. /// Gets or sets the index at the Global Color Table for the Background Color.
/// The Background Color is the color used for those /// The Background Color is the color used for those

2
src/ImageSharp/Formats/IImageDecoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;

21
src/ImageSharp/Formats/IImageInfoDetector.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Encapsulates methods used for detecting the raw image information without fully decoding it.
/// </summary>
public interface IImageInfoDetector
{
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <returns>The <see cref="PixelTypeInfo"/> object</returns>
IImageInfo Identify(Configuration configuration, Stream stream);
}
}

13
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// <summary> /// <summary>
/// Image decoder for generating an image out of a jpg stream. /// Image decoder for generating an image out of a jpg stream.
/// </summary> /// </summary>
internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -27,5 +27,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
return decoder.Decode<TPixel>(stream); return decoder.Decode<TPixel>(stream);
} }
} }
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
using (var decoder = new OrigJpegDecoderCore(configuration, this))
{
return decoder.Identify(stream);
}
}
} }
} }

25
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -31,6 +31,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </summary> /// </summary>
public const int MaxTq = 3; public const int MaxTq = 3;
/// <summary>
/// The only supported precision
/// </summary>
public const int SupportedPrecision = 8;
// Complex value type field + mutable + available to other classes = the field MUST NOT be private :P // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P
#pragma warning disable SA1401 // FieldsMustBePrivate #pragma warning disable SA1401 // FieldsMustBePrivate
@ -122,6 +127,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components; IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components;
/// <summary>
/// Gets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
/// <summary> /// <summary>
/// Gets the image height /// Gets the image height
/// </summary> /// </summary>
@ -173,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
public ImageMetaData MetaData { get; private set; } public ImageMetaData MetaData { get; private set; }
/// <summary> /// <summary>
/// Decodes the image from the specified <see cref="Stream"/> and sets /// Decodes the image from the specified <see cref="Stream"/> and sets
/// the data to image. /// the data to image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
@ -187,6 +197,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
return this.PostProcessIntoImage<TPixel>(); return this.PostProcessIntoImage<TPixel>();
} }
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream)
{
this.ParseStream(stream, true);
return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
}
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()
{ {
@ -622,7 +643,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.InputProcessor.ReadFull(this.Temp, 0, remaining); this.InputProcessor.ReadFull(this.Temp, 0, remaining);
// We only support 8-bit precision. // We only support 8-bit precision.
if (this.Temp[0] != 8) if (this.Temp[0] != SupportedPrecision)
{ {
throw new ImageFormatException("Only 8-Bit precision supported."); throw new ImageFormatException("Only 8-Bit precision supported.");
} }

14
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -4,7 +4,6 @@
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg namespace SixLabors.ImageSharp.Formats.Jpeg
@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Image decoder for generating an image out of a jpg stream. /// Image decoder for generating an image out of a jpg stream.
/// </summary> /// </summary>
public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -30,5 +29,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return decoder.Decode<TPixel>(stream); return decoder.Decode<TPixel>(stream);
} }
} }
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
using (var decoder = new OrigJpegDecoderCore(configuration, this))
{
return decoder.Identify(stream);
}
}
} }
} }

25
src/ImageSharp/Formats/PixelTypeInfo.cs

@ -0,0 +1,25 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Contains information about the pixels that make up an images visual data.
/// </summary>
public class PixelTypeInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelTypeInfo"/> class.
/// </summary>
/// <param name="bitsPerPixel">Color depth, in number of bits per pixel.</param>
internal PixelTypeInfo(int bitsPerPixel)
{
this.BitsPerPixel = bitsPerPixel;
}
/// <summary>
/// Gets color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel { get; }
}
}

11
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </list> /// </list>
/// </para> /// </para>
/// </remarks> /// </remarks>
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -54,5 +52,12 @@ namespace SixLabors.ImageSharp.Formats.Png
var decoder = new PngDecoderCore(configuration, this); var decoder = new PngDecoderCore(configuration, this);
return decoder.Decode<TPixel>(stream); return decoder.Decode<TPixel>(stream);
} }
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
var decoder = new PngDecoderCore(configuration, this);
return decoder.Identify(stream);
}
} }
} }

105
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -14,7 +14,6 @@ using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using static SixLabors.ImageSharp.ComparableExtensions;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
@ -237,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Png
deframeStream.AllocateNewBytes(currentChunk.Length); deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
stream.Read(this.crcBuffer, 0, 4); this.currentStream.Read(this.crcBuffer, 0, 4);
break; break;
case PngChunkTypes.Palette: case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length]; byte[] pal = new byte[currentChunk.Length];
@ -279,6 +278,66 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
} }
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream)
{
var metadata = new ImageMetaData();
this.currentStream = stream;
this.currentStream.Skip(8);
try
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
{
try
{
switch (currentChunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data);
break;
case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
break;
}
}
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
{
ArrayPool<byte>.Shared.Return(currentChunk.Data);
}
}
}
}
finally
{
this.scanline?.Dispose();
this.previousScanline?.Dispose();
}
if (this.header == null)
{
throw new ImageFormatException("PNG Image does not contain a header chunk");
}
return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
}
/// <summary> /// <summary>
/// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits. /// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits.
/// </summary> /// </summary>
@ -380,6 +439,28 @@ namespace SixLabors.ImageSharp.Formats.Png
this.scanline = this.configuration.MemoryManager.Allocate<byte>(this.bytesPerScanline, true); this.scanline = this.configuration.MemoryManager.Allocate<byte>(this.bytesPerScanline, true);
} }
/// <summary>
/// Calculates the correct number of bits per pixel for the given color type.
/// </summary>
/// <returns>The <see cref="int"/></returns>
private int CalculateBitsPerPixel()
{
switch (this.pngColorType)
{
case PngColorType.Grayscale:
case PngColorType.Palette:
return this.header.BitDepth;
case PngColorType.GrayscaleWithAlpha:
return this.header.BitDepth * 2;
case PngColorType.Rgb:
return this.header.BitDepth * 3;
case PngColorType.RgbWithAlpha:
return this.header.BitDepth * 4;
default:
throw new NotSupportedException("Unsupported PNG color type");
}
}
/// <summary> /// <summary>
/// Calculates the correct number of bytes per pixel for the given color type. /// Calculates the correct number of bytes per pixel for the given color type.
/// </summary> /// </summary>
@ -508,7 +589,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.ProcessDefilteredScanline(this.scanline.Array, image); this.ProcessDefilteredScanline(this.scanline.Array, image);
Swap(ref this.scanline, ref this.previousScanline); this.SwapBuffers();
this.currentRow++; this.currentRow++;
} }
} }
@ -584,7 +665,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
Swap(ref this.scanline, ref this.previousScanline); this.SwapBuffers();
this.currentRow += Adam7RowIncrement[this.pass]; this.currentRow += Adam7RowIncrement[this.pass];
} }
@ -1182,6 +1263,15 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
} }
/// <summary>
/// Skips the chunk data and the cycle redundancy chunk read from the data.
/// </summary>
private void SkipChunkDataAndCrc(PngChunk chunk)
{
this.currentStream.Skip(chunk.Length);
this.currentStream.Skip(4);
}
/// <summary> /// <summary>
/// Reads the chunk data from the stream. /// Reads the chunk data from the stream.
/// </summary> /// </summary>
@ -1258,5 +1348,12 @@ namespace SixLabors.ImageSharp.Formats.Png
default: throw new ArgumentException($"Not a valid pass index: {passIndex}"); default: throw new ArgumentException($"Not a valid pass index: {passIndex}");
} }
} }
private void SwapBuffers()
{
Buffer<byte> temp = this.previousScanline;
this.previousScanline = this.scanline;
this.scanline = temp;
}
} }
} }

2
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public bool IgnoreMetadata { get; set; } public bool IgnoreMetadata { get; set; }
/// <summary> /// <summary>
/// Gets or sets the size of the color palette to use. Set to zero to leav png encoding to use pixel data. /// Gets or sets the size of the color palette to use. Set to zero to leave png encoding to use pixel data.
/// </summary> /// </summary>
public int PaletteSize { get; set; } = 0; public int PaletteSize { get; set; } = 0;

7
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -11,7 +11,6 @@ using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers; using SixLabors.ImageSharp.Quantizers;
using static SixLabors.ImageSharp.ComparableExtensions;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
@ -151,7 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Initializes a new instance of the <see cref="PngEncoderCore"/> class. /// Initializes a new instance of the <see cref="PngEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="options">The options for influancing the encoder</param> /// <param name="options">The options for influencing the encoder</param>
public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options) public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options)
{ {
this.memoryManager = memoryManager; this.memoryManager = memoryManager;
@ -643,7 +642,9 @@ namespace SixLabors.ImageSharp.Formats.Png
Buffer<byte> r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y); Buffer<byte> r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y);
deflateStream.Write(r.Array, 0, resultLength); deflateStream.Write(r.Array, 0, resultLength);
Swap(ref this.rawScanline, ref this.previousScanline); Buffer<byte> temp = this.rawScanline;
this.rawScanline = this.previousScanline;
this.previousScanline = temp;
} }
} }

12
src/ImageSharp/Image/IImage.cs

@ -0,0 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates the properties and methods that describe an image.
/// </summary>
public interface IImage : IImageInfo
{
}
}

35
src/ImageSharp/Image/IImageInfo.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates properties that descibe basic image information including dimensions, pixel type information
/// and additional metadata
/// </summary>
public interface IImageInfo
{
/// <summary>
/// Gets information about the image pixels.
/// </summary>
PixelTypeInfo PixelType { get; }
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the metadata of the image.
/// </summary>
ImageMetaData MetaData { get; }
}
}

14
src/ImageSharp/Image/Image.Decode.cs

@ -79,5 +79,19 @@ namespace SixLabors.ImageSharp
Image<TPixel> img = decoder.Decode<TPixel>(config, stream); Image<TPixel> img = decoder.Decode<TPixel>(config, stream);
return (img, format); return (img, format);
} }
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="config">the configuration.</param>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
private static IImageInfo InternalIdentity(Stream stream, Configuration config)
{
var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector;
return detector?.Identify(config, stream);
}
} }
} }

31
src/ImageSharp/Image/Image.FromStream.cs

@ -36,6 +36,37 @@ namespace SixLabors.ImageSharp
return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default)); return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default));
} }
/// <summary>
/// By reading the header on the provided stream this reads the raw image information.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector not found.
/// </returns>
public static IImageInfo Identify(Stream stream)
{
return Identify(null, stream);
}
/// <summary>
/// Reads the raw image information from the specified stream without fully decoding it.
/// </summary>
/// <param name="config">The configuration.</param>
/// <param name="stream">The image stream to read the information from.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>
/// The <see cref="IImageInfo"/> or null if suitable info detector is not found.
/// </returns>
public static IImageInfo Identify(Configuration config, Stream stream)
{
return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default));
}
/// <summary> /// <summary>
/// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream. /// Create a new instance of the <see cref="Image{Rgba32}"/> class from the given stream.
/// </summary> /// </summary>

16
src/ImageSharp/Image/ImageFrame{TPixel}.cs

@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
@ -60,7 +61,16 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="size">The <see cref="Size"/> of the frame.</param>
/// <param name="metaData">The meta data.</param>
internal ImageFrame(Size size, ImageFrameMetaData metaData)
: this(size.Width, size.Height, metaData)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
internal ImageFrame(MemoryManager memoryManager, ImageFrame<TPixel> source) internal ImageFrame(MemoryManager memoryManager, ImageFrame<TPixel> source)
{ {
@ -168,7 +178,9 @@ namespace SixLabors.ImageSharp
{ {
Guard.NotNull(pixelSource, nameof(pixelSource)); Guard.NotNull(pixelSource, nameof(pixelSource));
ComparableExtensions.Swap(ref this.pixelBuffer, ref pixelSource.pixelBuffer); Buffer2D<TPixel> temp = this.pixelBuffer;
this.pixelBuffer = pixelSource.pixelBuffer;
pixelSource.pixelBuffer = temp;
} }
/// <summary> /// <summary>

41
src/ImageSharp/Image/ImageInfo.cs

@ -0,0 +1,41 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Contains information about the image including dimensions, pixel type information and additional metadata
/// </summary>
internal sealed class ImageInfo : IImageInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageInfo"/> class.
/// </summary>
/// <param name="pixelType">The image pixel type information.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metaData">The images metadata.</param>
public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData)
{
this.PixelType = pixelType;
this.Width = width;
this.Height = height;
this.MetaData = metaData;
}
/// <inheritdoc />
public PixelTypeInfo PixelType { get; }
/// <inheritdoc />
public int Width { get; }
/// <inheritdoc />
public int Height { get; }
/// <inheritdoc />
public ImageMetaData MetaData { get; }
}
}

21
src/ImageSharp/Image/Image{TPixel}.cs

@ -5,9 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp
/// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed partial class Image<TPixel> : IDisposable, IConfigurable public sealed partial class Image<TPixel> : IImage, IDisposable, IConfigurable
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private Configuration configuration; private Configuration configuration;
@ -61,6 +61,7 @@ namespace SixLabors.ImageSharp
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) internal Image(Configuration configuration, int width, int height, ImageMetaData metadata)
{ {
this.configuration = configuration ?? Configuration.Default; this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata ?? new ImageMetaData(); this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, width, height); this.frames = new ImageFrameCollection<TPixel>(this, width, height);
} }
@ -75,6 +76,7 @@ namespace SixLabors.ImageSharp
internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames) internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames)
{ {
this.configuration = configuration ?? Configuration.Default; this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata ?? new ImageMetaData(); this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, frames); this.frames = new ImageFrameCollection<TPixel>(this, frames);
@ -85,19 +87,16 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
Configuration IConfigurable.Configuration => this.configuration; Configuration IConfigurable.Configuration => this.configuration;
/// <summary> /// <inheritdoc/>
/// Gets the width. public PixelTypeInfo PixelType { get; }
/// </summary>
/// <inheritdoc/>
public int Width => this.frames.RootFrame.Width; public int Width => this.frames.RootFrame.Width;
/// <summary> /// <inheritdoc/>
/// Gets the height.
/// </summary>
public int Height => this.frames.RootFrame.Height; public int Height => this.frames.RootFrame.Height;
/// <summary> /// <inheritdoc/>
/// Gets the meta data of the image.
/// </summary>
public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); public ImageMetaData MetaData { get; private set; } = new ImageMetaData();
/// <summary> /// <summary>

1
src/ImageSharp/ImageSharp.csproj

@ -35,6 +35,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0004" /> <PackageReference Include="SixLabors.Core" Version="1.0.0-beta0004" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-ci0005" />
<AdditionalFiles Include="..\..\stylecop.json" /> <AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>

12
src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs

@ -290,9 +290,11 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>The <see cref="bool"/></returns> /// <returns>The <see cref="bool"/></returns>
private static bool IsStandardNormalizedType(Type type) private static bool IsStandardNormalizedType(Type type)
{ {
return type == typeof(Rgba32) return
type == typeof(Alpha8)
|| type == typeof(Argb32) || type == typeof(Argb32)
|| type == typeof(Alpha8) || type == typeof(Bgr24)
|| type == typeof(Bgra32)
|| type == typeof(Bgr565) || type == typeof(Bgr565)
|| type == typeof(Bgra4444) || type == typeof(Bgra4444)
|| type == typeof(Bgra5551) || type == typeof(Bgra5551)
@ -300,8 +302,10 @@ namespace SixLabors.ImageSharp.PixelFormats
|| type == typeof(HalfVector2) || type == typeof(HalfVector2)
|| type == typeof(HalfVector4) || type == typeof(HalfVector4)
|| type == typeof(Rg32) || type == typeof(Rg32)
|| type == typeof(Rgba1010102) || type == typeof(Rgb24)
|| type == typeof(Rgba64); || type == typeof(Rgba32)
|| type == typeof(Rgba64)
|| type == typeof(Rgba1010102);
} }
/// <summary> /// <summary>

6
src/ImageSharp/PixelFormats/Rgb24.cs

@ -126,5 +126,11 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.B = this.B; dest.B = this.B;
dest.A = 255; dest.A = 255;
} }
/// <inheritdoc/>
public override string ToString()
{
return $"({this.R},{this.G},{this.B})";
}
} }
} }

2
src/ImageSharp/PixelFormats/Rgba32.cs

@ -364,7 +364,7 @@ namespace SixLabors.ImageSharp
/// <returns>A string representation of the packed vector.</returns> /// <returns>A string representation of the packed vector.</returns>
public override string ToString() public override string ToString()
{ {
return this.ToVector4().ToString(); return $"({this.R},{this.G},{this.B},{this.A})";
} }
/// <inheritdoc/> /// <inheritdoc/>

4
src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs

@ -1,9 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -43,4 +41,4 @@ namespace SixLabors.ImageSharp
return source; return source;
} }
} }
} }

16
src/ImageSharp/Processing/Effects/Brightness.cs → src/ImageSharp/Processing/ColorMatrix/Brightness.cs

@ -16,25 +16,33 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Alters the brightness component of the image. /// Alters the brightness component of the image.
/// </summary> /// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param> /// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Brightness<TPixel>(this IImageProcessingContext<TPixel> source, int amount) public static IImageProcessingContext<TPixel> Brightness<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new BrightnessProcessor<TPixel>(amount)); => source.ApplyProcessor(new BrightnessProcessor<TPixel>(amount));
/// <summary> /// <summary>
/// Alters the brightness component of the image. /// Alters the brightness component of the image.
/// </summary> /// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param> /// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Brightness<TPixel>(this IImageProcessingContext<TPixel> source, int amount, Rectangle rectangle) public static IImageProcessingContext<TPixel> Brightness<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new BrightnessProcessor<TPixel>(amount), rectangle); => source.ApplyProcessor(new BrightnessProcessor<TPixel>(amount), rectangle);
} }

19
src/ImageSharp/Processing/Effects/Contrast.cs → src/ImageSharp/Processing/ColorMatrix/Contrast.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -16,26 +15,34 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Alters the contrast component of the image. /// Alters the contrast component of the image.
/// </summary> /// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param> /// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Contrast<TPixel>(this IImageProcessingContext<TPixel> source, int amount) public static IImageProcessingContext<TPixel> Contrast<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new ContrastProcessor<TPixel>(amount)); => source.ApplyProcessor(new ContrastProcessor<TPixel>(amount));
/// <summary> /// <summary>
/// Alters the contrast component of the image. /// Alters the contrast component of the image.
/// </summary> /// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param> /// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Contrast<TPixel>(this IImageProcessingContext<TPixel> source, int amount, Rectangle rectangle) public static IImageProcessingContext<TPixel> Contrast<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new ContrastProcessor<TPixel>(amount), rectangle); => source.ApplyProcessor(new ContrastProcessor<TPixel>(amount), rectangle);
} }
} }

21
src/ImageSharp/Processing/ColorMatrix/Saturation.cs → src/ImageSharp/Processing/ColorMatrix/Filter.cs

@ -1,9 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -15,34 +14,34 @@ namespace SixLabors.ImageSharp
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Alters the saturation component of the image. /// Filters an image but the given color matrix
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param> /// <param name="matrix">The filter color matrix</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Saturation<TPixel>(this IImageProcessingContext<TPixel> source, int amount) public static IImageProcessingContext<TPixel> Filter<TPixel>(this IImageProcessingContext<TPixel> source, Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
source.ApplyProcessor(new SaturationProcessor<TPixel>(amount)); source.ApplyProcessor(new FilterProcessor<TPixel>(matrix));
return source; return source;
} }
/// <summary> /// <summary>
/// Alters the saturation component of the image. /// Filters an image but the given color matrix
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param> /// <param name="matrix">The filter color matrix</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Saturation<TPixel>(this IImageProcessingContext<TPixel> source, int amount, Rectangle rectangle) public static IImageProcessingContext<TPixel> Filter<TPixel>(this IImageProcessingContext<TPixel> source, Matrix4x4 matrix, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
source.ApplyProcessor(new SaturationProcessor<TPixel>(amount), rectangle); source.ApplyProcessor(new FilterProcessor<TPixel>(matrix), rectangle);
return source; return source;
} }
} }
} }

72
src/ImageSharp/Processing/ColorMatrix/Grayscale.cs

@ -21,12 +21,21 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source) public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ => Grayscale(source, GrayscaleMode.Bt709);
return Grayscale(source, GrayscaleMode.Bt709);
}
/// <summary> /// <summary>
/// Applies Grayscale toning to the image. /// Applies <see cref="GrayscaleMode.Bt709"/> Grayscale toning to the image using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel>
=> Grayscale(source, GrayscaleMode.Bt709, amount);
/// <summary>
/// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/>.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
@ -34,10 +43,22 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, GrayscaleMode mode) public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, GrayscaleMode mode)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> Grayscale(source, mode, 1F);
/// <summary>
/// Applies grayscale toning to the image with the given <see cref="GrayscaleMode"/> using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, GrayscaleMode mode, float amount)
where TPixel : struct, IPixel<TPixel>
{ {
IImageProcessor<TPixel> processor = mode == GrayscaleMode.Bt709 IImageProcessor<TPixel> processor = mode == GrayscaleMode.Bt709
? (IImageProcessor<TPixel>)new GrayscaleBt709Processor<TPixel>() ? (IImageProcessor<TPixel>)new GrayscaleBt709Processor<TPixel>(amount)
: new GrayscaleBt601Processor<TPixel>(); : new GrayscaleBt601Processor<TPixel>(1F);
source.ApplyProcessor(processor); source.ApplyProcessor(processor);
return source; return source;
@ -54,9 +75,21 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, Rectangle rectangle) public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ => Grayscale(source, 1F, rectangle);
return Grayscale(source, GrayscaleMode.Bt709, rectangle);
} /// <summary>
/// Applies <see cref="GrayscaleMode.Bt709"/> Grayscale toning to the image using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
=> Grayscale(source, GrayscaleMode.Bt709, amount, rectangle);
/// <summary> /// <summary>
/// Applies Grayscale toning to the image. /// Applies Grayscale toning to the image.
@ -70,13 +103,28 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, GrayscaleMode mode, Rectangle rectangle) public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, GrayscaleMode mode, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> Grayscale(source, mode, 1F, rectangle);
/// <summary>
/// Applies Grayscale toning to the image using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Grayscale<TPixel>(this IImageProcessingContext<TPixel> source, GrayscaleMode mode, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{ {
IImageProcessor<TPixel> processor = mode == GrayscaleMode.Bt709 IImageProcessor<TPixel> processor = mode == GrayscaleMode.Bt709
? (IImageProcessor<TPixel>)new GrayscaleBt709Processor<TPixel>() ? (IImageProcessor<TPixel>)new GrayscaleBt709Processor<TPixel>(amount)
: new GrayscaleBt601Processor<TPixel>(); : new GrayscaleBt601Processor<TPixel>(amount);
source.ApplyProcessor(processor, rectangle); source.ApplyProcessor(processor, rectangle);
return source; return source;
} }
} }
} }

6
src/ImageSharp/Processing/ColorMatrix/Hue.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param> /// <param name="degrees">The rotation angle in degrees to adjust the hue.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Hue<TPixel>(this IImageProcessingContext<TPixel> source, float degrees) public static IImageProcessingContext<TPixel> Hue<TPixel>(this IImageProcessingContext<TPixel> source, float degrees)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param> /// <param name="degrees">The rotation angle in degrees to adjust the hue.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
@ -45,4 +45,4 @@ namespace SixLabors.ImageSharp
return source; return source;
} }
} }
} }

446
src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs

@ -0,0 +1,446 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides extensions methods for the <see cref="Matrix4x4"/> struct
/// </summary>
public static class MatrixFilters
{
/// <summary>
/// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness
/// </summary>
public static Matrix4x4 AchromatomalyFilter { get; } = new Matrix4x4
{
M11 = .618F,
M12 = .163F,
M13 = .163F,
M21 = .320F,
M22 = .775F,
M23 = .320F,
M31 = .062F,
M32 = .062F,
M33 = .516F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
public static Matrix4x4 AchromatopsiaFilter { get; } = new Matrix4x4
{
M11 = .299F,
M12 = .299F,
M13 = .299F,
M21 = .587F,
M22 = .587F,
M23 = .587F,
M31 = .114F,
M32 = .114F,
M33 = .114F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness.
/// </summary>
public static Matrix4x4 DeuteranomalyFilter { get; } = new Matrix4x4
{
M11 = 0.8F,
M12 = 0.258F,
M21 = 0.2F,
M22 = 0.742F,
M23 = 0.142F,
M33 = 0.858F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Deuteranopia (Green-Blind) color blindness.
/// </summary>
public static Matrix4x4 DeuteranopiaFilter { get; } = new Matrix4x4
{
M11 = 0.625F,
M12 = 0.7F,
M21 = 0.375F,
M22 = 0.3F,
M23 = 0.3F,
M33 = 0.7F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Protanomaly (Red-Weak) color blindness.
/// </summary>
public static Matrix4x4 ProtanomalyFilter { get; } = new Matrix4x4
{
M11 = 0.817F,
M12 = 0.333F,
M21 = 0.183F,
M22 = 0.667F,
M23 = 0.125F,
M33 = 0.875F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Protanopia (Red-Blind) color blindness.
/// </summary>
public static Matrix4x4 ProtanopiaFilter { get; } = new Matrix4x4
{
M11 = 0.567F,
M12 = 0.558F,
M21 = 0.433F,
M22 = 0.442F,
M23 = 0.242F,
M33 = 0.758F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness.
/// </summary>
public static Matrix4x4 TritanomalyFilter { get; } = new Matrix4x4
{
M11 = 0.967F,
M21 = 0.33F,
M22 = 0.733F,
M23 = 0.183F,
M32 = 0.267F,
M33 = 0.817F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Tritanopia (Blue-Blind) color blindness.
/// </summary>
public static Matrix4x4 TritanopiaFilter { get; } = new Matrix4x4
{
M11 = 0.95F,
M21 = 0.05F,
M22 = 0.433F,
M23 = 0.475F,
M32 = 0.567F,
M33 = 0.525F,
M44 = 1
};
/// <summary>
/// Gets an approximated black and white filter
/// </summary>
public static Matrix4x4 BlackWhiteFilter { get; } = new Matrix4x4()
{
M11 = 1.5F,
M12 = 1.5F,
M13 = 1.5F,
M21 = 1.5F,
M22 = 1.5F,
M23 = 1.5F,
M31 = 1.5F,
M32 = 1.5F,
M33 = 1.5F,
M41 = -1F,
M42 = -1F,
M43 = -1F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating an old Kodachrome camera effect.
/// </summary>
public static Matrix4x4 KodachromeFilter { get; } = new Matrix4x4
{
M11 = 0.7297023F,
M22 = 0.6109577F,
M33 = 0.597218F,
M41 = 0.105F,
M42 = 0.145F,
M43 = 0.155F,
M44 = 1
}
* CreateSaturateFilter(1.2F) * CreateContrastFilter(1.35F);
/// <summary>
/// Gets a filter recreating an old Lomograph camera effect.
/// </summary>
public static Matrix4x4 LomographFilter { get; } = new Matrix4x4
{
M11 = 1.5F,
M22 = 1.45F,
M33 = 1.16F,
M41 = -.1F,
M42 = -.02F,
M43 = -.07F,
M44 = 1
}
* CreateSaturateFilter(1.1F) * CreateContrastFilter(1.33F);
/// <summary>
/// Gets a filter recreating an old Polaroid camera effect.
/// </summary>
public static Matrix4x4 PolaroidFilter { get; } = new Matrix4x4
{
M11 = 1.538F,
M12 = -0.062F,
M13 = -0.262F,
M21 = -0.022F,
M22 = 1.578F,
M23 = -0.022F,
M31 = .216F,
M32 = -.16F,
M33 = 1.5831F,
M41 = 0.02F,
M42 = -0.05F,
M43 = -0.05F,
M44 = 1
};
/// <summary>
/// Create a brightness filter matrix using the given amount.
/// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateBrightnessFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = amount,
M22 = amount,
M33 = amount,
M44 = 1
};
}
/// <summary>
/// Create a contrast filter matrix using the given amount.
/// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateContrastFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
float contrast = (-.5F * amount) + .5F;
return new Matrix4x4
{
M11 = amount,
M22 = amount,
M33 = amount,
M41 = contrast,
M42 = contrast,
M43 = contrast,
M44 = 1
};
}
/// <summary>
/// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.601.
/// <see href="https://en.wikipedia.org/wiki/Luma_%28video%29#Rec._601_luma_versus_Rec._709_luma_coefficients"/>
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateGrayscaleBt601Filter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .299F + (.701F * amount),
M12 = .299F - (.299F * amount),
M13 = .299F - (.299F * amount),
M21 = .587F - (.587F * amount),
M22 = .587F + (.413F * amount),
M23 = .587F - (.587F * amount),
M31 = .114F - (.114F * amount),
M32 = .114F - (.114F * amount),
M33 = .114F + (.886F * amount),
M44 = 1
};
}
/// <summary>
/// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.709.
/// <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateGrayscaleBt709Filter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .2126F + (.7874F * amount),
M12 = .2126F - (.2126F * amount),
M13 = .2126F - (.2126F * amount),
M21 = .7152F - (.7152F * amount),
M22 = .7152F + (.2848F * amount),
M23 = .7152F - (.7152F * amount),
M31 = .0722F - (.0722F * amount),
M32 = .0722F - (.0722F * amount),
M33 = .0722F + (.9278F * amount),
M44 = 1
};
}
/// <summary>
/// Create a hue filter matrix using the given angle in degrees.
/// </summary>
/// <param name="degrees">The angle of rotation in degrees.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateHueFilter(float degrees)
{
// Wrap the angle round at 360.
degrees = degrees % 360;
// Make sure it's not negative.
while (degrees < 0)
{
degrees += 360;
}
float radian = MathFExtensions.DegreeToRadian(degrees);
float cosRadian = MathF.Cos(radian);
float sinRadian = MathF.Sin(radian);
// The matrix is set up to preserve the luminance of the image.
// See http://graficaobscura.com/matrix/index.html
// Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
return new Matrix4x4
{
M11 = .213F + (cosRadian * .787F) - (sinRadian * .213F),
M12 = .213F - (cosRadian * .213F) - (sinRadian * 0.143F),
M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F),
M21 = .715F - (cosRadian * .715F) - (sinRadian * .715F),
M22 = .715F + (cosRadian * .285F) + (sinRadian * 0.140F),
M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F),
M31 = .072F - (cosRadian * .072F) + (sinRadian * .928F),
M32 = .072F - (cosRadian * .072F) - (sinRadian * 0.283F),
M33 = .072F + (cosRadian * .928F) + (sinRadian * .072F),
M44 = 1
};
}
/// <summary>
/// Create an invert filter matrix using the given amount.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateInvertFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
float invert = 1F - (2F * amount);
return new Matrix4x4
{
M11 = invert,
M22 = invert,
M33 = invert,
M41 = amount,
M42 = amount,
M43 = amount,
M44 = 1
};
}
/// <summary>
/// Create an opacity filter matrix using the given amount.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateOpacityFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = 1,
M22 = 1,
M33 = 1,
M44 = amount
};
}
/// <summary>
/// Create a saturation filter matrix using the given amount.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateSaturateFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .213F + (.787F * amount),
M12 = .213F - (.213F * amount),
M13 = .213F - (.213F * amount),
M21 = .715F - (.715F * amount),
M22 = .715F + (.285F * amount),
M23 = .715F - (.715F * amount),
M31 = 1F - ((.213F + (.787F * amount)) + (.715F - (.715F * amount))),
M32 = 1F - ((.213F - (.213F * amount)) + (.715F + (.285F * amount))),
M33 = 1F - ((.213F - (.213F * amount)) + (.715F - (.715F * amount))),
M44 = 1
};
}
/// <summary>
/// Create a sepia filter matrix using the given amount.
/// The formula used matches the svg specification. <see href="http://www.w3.org/TR/filter-effects/#sepiaEquivalent"/>
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateSepiaFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .393F + (.607F * amount),
M12 = .349F - (.349F * amount),
M13 = .272F - (.272F * amount),
M21 = .769F - (.769F * amount),
M22 = .686F + (.314F * amount),
M23 = .534F - (.534F * amount),
M31 = .189F - (.189F * amount),
M32 = .168F - (.168F * amount),
M33 = .131F + (.869F * amount),
M44 = 1
};
}
}
}

14
src/ImageSharp/Processing/Effects/Alpha.cs → src/ImageSharp/Processing/ColorMatrix/Opacity.cs

@ -18,24 +18,24 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 1.</param> /// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Alpha<TPixel>(this IImageProcessingContext<TPixel> source, float percent) public static IImageProcessingContext<TPixel> Opacity<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new AlphaProcessor<TPixel>(percent)); => source.ApplyProcessor(new OpacityProcessor<TPixel>(amount));
/// <summary> /// <summary>
/// Alters the alpha component of the image. /// Alters the alpha component of the image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 1.</param> /// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Alpha<TPixel>(this IImageProcessingContext<TPixel> source, float percent, Rectangle rectangle) public static IImageProcessingContext<TPixel> Opacity<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new AlphaProcessor<TPixel>(percent), rectangle); => source.ApplyProcessor(new OpacityProcessor<TPixel>(amount), rectangle);
} }
} }

54
src/ImageSharp/Processing/ColorMatrix/Saturate.cs

@ -0,0 +1,54 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Saturate<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new SaturateProcessor<TPixel>(amount));
return source;
}
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Saturate<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new SaturateProcessor<TPixel>(amount), rectangle);
return source;
}
}
}

31
src/ImageSharp/Processing/ColorMatrix/Sepia.cs

@ -1,9 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -22,7 +20,18 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Sepia<TPixel>(this IImageProcessingContext<TPixel> source) public static IImageProcessingContext<TPixel> Sepia<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SepiaProcessor<TPixel>()); => Sepia(source, 1F);
/// <summary>
/// Applies sepia toning to the image using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Sepia<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SepiaProcessor<TPixel>(amount));
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
@ -35,6 +44,20 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Sepia<TPixel>(this IImageProcessingContext<TPixel> source, Rectangle rectangle) public static IImageProcessingContext<TPixel> Sepia<TPixel>(this IImageProcessingContext<TPixel> source, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SepiaProcessor<TPixel>(), rectangle); => Sepia(source, 1F, rectangle);
/// <summary>
/// Applies sepia toning to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Sepia<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SepiaProcessor<TPixel>(amount), rectangle);
} }
} }

4
src/ImageSharp/Processing/Effects/Invert.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Invert<TPixel>(this IImageProcessingContext<TPixel> source) public static IImageProcessingContext<TPixel> Invert<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new InvertProcessor<TPixel>()); => source.ApplyProcessor(new InvertProcessor<TPixel>(1F));
/// <summary> /// <summary>
/// Inverts the colors of the image. /// Inverts the colors of the image.
@ -34,6 +34,6 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Invert<TPixel>(this IImageProcessingContext<TPixel> source, Rectangle rectangle) public static IImageProcessingContext<TPixel> Invert<TPixel>(this IImageProcessingContext<TPixel> source, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new InvertProcessor<TPixel>(), rectangle); => source.ApplyProcessor(new InvertProcessor<TPixel>(1F), rectangle);
} }
} }

2
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration); new GrayscaleBt709Processor<TPixel>(1F).Apply(source, sourceRectangle, configuration);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs

@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration); new GrayscaleBt709Processor<TPixel>(1F).Apply(source, sourceRectangle, configuration);
} }
/// <inheritdoc/> /// <inheritdoc/>

2
src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration); new GrayscaleBt709Processor<TPixel>(1F).Apply(source, sourceRectangle, configuration);
} }
/// <inheritdoc/> /// <inheritdoc/>

6
src/ImageSharp/Processing/Processors/CloningImageProcessor.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing
if (clone.Frames.Count != source.Frames.Count) if (clone.Frames.Count != source.Frames.Count)
{ {
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames.");
} }
Configuration configuration = source.GetConfiguration(); Configuration configuration = source.GetConfiguration();
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing
// we now need to move the pixel data/size data from one image base to another // we now need to move the pixel data/size data from one image base to another
if (cloned.Frames.Count != source.Frames.Count) if (cloned.Frames.Count != source.Frames.Count)
{ {
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames.");
} }
source.SwapPixelsBuffers(cloned); source.SwapPixelsBuffers(cloned);
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing
} }
/// <summary> /// <summary>
/// Generates a deep clone of the source image that operatinos should be applied to. /// Generates a deep clone of the source image that operations should be applied to.
/// </summary> /// </summary>
/// <param name="source">The source image. Cannot be null.</param> /// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The source rectangle.</param> /// <param name="sourceRectangle">The source rectangle.</param>

34
src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image to their black and white equivalent.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BlackWhiteProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4()
{
M11 = 1.5F,
M12 = 1.5F,
M13 = 1.5F,
M21 = 1.5F,
M22 = 1.5F,
M23 = 1.5F,
M31 = 1.5F,
M32 = 1.5F,
M33 = 1.5F,
M41 = -1F,
M42 = -1F,
M43 = -1F,
M44 = 1
};
}
}

34
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .618F,
M12 = .163F,
M13 = .163F,
M21 = .320F,
M22 = .775F,
M23 = .320F,
M31 = .062F,
M32 = .062F,
M33 = .516F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

34
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatopsiaProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .299F,
M12 = .299F,
M13 = .299F,
M21 = .587F,
M22 = .587F,
M23 = .587F,
M31 = .114F,
M32 = .114F,
M33 = .114F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

31
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProtanomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = 0.817F,
M12 = 0.333F,
M21 = 0.183F,
M22 = 0.667F,
M23 = 0.125F,
M33 = 0.875F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

78
src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs

@ -1,78 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// The color matrix filter. Inherit from this class to perform operation involving color matrices.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal abstract class ColorMatrixProcessor<TPixel> : ImageProcessor<TPixel>, IColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public abstract Matrix4x4 Matrix { get; }
/// <inheritdoc/>
public virtual bool Compand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
Matrix4x4 matrix = this.Matrix;
bool compand = this.Compand;
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);
for (int x = minX; x < maxX; x++)
{
ref TPixel pixel = ref row[x - startX];
var vector = pixel.ToVector4();
if (compand)
{
vector = vector.Expand();
}
vector = Vector4.Transform(vector, matrix);
pixel.PackFromVector4(compand ? vector.Compress() : vector);
}
});
}
}
}

32
src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs

@ -1,32 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.601
/// <see href="https://en.wikipedia.org/wiki/Luma_%28video%29#Rec._601_luma_versus_Rec._709_luma_coefficients"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GrayscaleBt601Processor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .299F,
M12 = .299F,
M13 = .299F,
M21 = .587F,
M22 = .587F,
M23 = .587F,
M31 = .114F,
M32 = .114F,
M33 = .114F,
M44 = 1
};
}
}

32
src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs

@ -1,32 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709
/// <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GrayscaleBt709Processor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .2126F,
M12 = .2126F,
M13 = .2126F,
M21 = .7152F,
M22 = .7152F,
M23 = .7152F,
M31 = .0722F,
M32 = .0722F,
M33 = .0722F,
M44 = 1
};
}
}

77
src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs

@ -1,77 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="ImageProcessor{TPixel}"/> to change the hue of an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class HueProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="HueProcessor{TPixel}"/> class.
/// </summary>
/// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param>
public HueProcessor(float angle)
{
// Wrap the angle round at 360.
angle = angle % 360;
// Make sure it's not negative.
while (angle < 0)
{
angle += 360;
}
this.Angle = angle;
float radians = MathFExtensions.DegreeToRadian(angle);
float cosradians = MathF.Cos(radians);
float sinradians = MathF.Sin(radians);
float lumR = .213F;
float lumG = .715F;
float lumB = .072F;
float oneMinusLumR = 1 - lumR;
float oneMinusLumG = 1 - lumG;
float oneMinusLumB = 1 - lumB;
// The matrix is set up to preserve the luminance of the image.
// See http://graficaobscura.com/matrix/index.html
// Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
var matrix4X4 = new Matrix4x4
{
M11 = lumR + (cosradians * oneMinusLumR) - (sinradians * lumR),
M12 = lumR - (cosradians * lumR) - (sinradians * 0.143F),
M13 = lumR - (cosradians * lumR) - (sinradians * oneMinusLumR),
M21 = lumG - (cosradians * lumG) - (sinradians * lumG),
M22 = lumG + (cosradians * oneMinusLumG) + (sinradians * 0.140F),
M23 = lumG - (cosradians * lumG) + (sinradians * lumG),
M31 = lumB - (cosradians * lumB) + (sinradians * oneMinusLumB),
M32 = lumB - (cosradians * lumB) - (sinradians * 0.283F),
M33 = lumB + (cosradians * oneMinusLumB) + (sinradians * lumB),
M44 = 1
};
this.Matrix = matrix4X4;
}
/// <summary>
/// Gets the rotation value.
/// </summary>
public float Angle { get; }
/// <inheritdoc/>
public override Matrix4x4 Matrix { get; }
/// <inheritdoc/>
public override bool Compand => false;
}
}

27
src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs

@ -1,27 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Encapsulates properties and methods for creating processors that utilize a matrix to
/// alter the image pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal interface IColorMatrixProcessor<TPixel> : IImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the <see cref="Matrix4x4"/> used to alter the image.
/// </summary>
Matrix4x4 Matrix { get; }
/// <summary>
/// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing.
/// </summary>
bool Compand { get; set; }
}
}

28
src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs

@ -1,28 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating an old Kodachrome camera effect.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class KodachromeProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = 0.6997023F,
M22 = 0.4609577F,
M33 = 0.397218F,
M41 = 0.005F,
M42 = -0.005F,
M43 = 0.005F,
M44 = 1
};
}
}

66
src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs

@ -1,66 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="ImageProcessor{TPixel}"/> to change the saturation of an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class SaturationProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="SaturationProcessor{TPixel}"/> class.
/// </summary>
/// <param name="saturation">The new saturation of the image. Must be between -100 and 100.</param>
/// <exception cref="System.ArgumentException">
/// <paramref name="saturation"/> is less than -100 or is greater than 100.
/// </exception>
public SaturationProcessor(int saturation)
{
this.Amount = saturation;
Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation));
float saturationFactor = saturation / 100F;
// Stop at -1 to prevent inversion.
saturationFactor++;
// The matrix is set up to "shear" the color space using the following set of values.
// Note that each color component has an effective luminance which contributes to the
// overall brightness of the pixel.
// See http://graficaobscura.com/matrix/index.html
float saturationComplement = 1.0f - saturationFactor;
float saturationComplementR = 0.3086f * saturationComplement;
float saturationComplementG = 0.6094f * saturationComplement;
float saturationComplementB = 0.0820f * saturationComplement;
var matrix4X4 = new Matrix4x4
{
M11 = saturationComplementR + saturationFactor,
M12 = saturationComplementR,
M13 = saturationComplementR,
M21 = saturationComplementG,
M22 = saturationComplementG + saturationFactor,
M23 = saturationComplementG,
M31 = saturationComplementB,
M32 = saturationComplementB,
M33 = saturationComplementB + saturationFactor,
M44 = 1
};
this.Matrix = matrix4X4;
}
/// <summary>
/// Gets the amount to apply.
/// </summary>
public int Amount { get; }
/// <inheritdoc/>
public override Matrix4x4 Matrix { get; }
}
}

35
src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image to their sepia equivalent.
/// The formula used matches the svg specification. <see href="http://www.w3.org/TR/filter-effects/#sepiaEquivalent"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class SepiaProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .393F,
M12 = .349F,
M13 = .272F,
M21 = .769F,
M22 = .686F,
M23 = .534F,
M31 = .189F,
M32 = .168F,
M33 = .131F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

4
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
int offsetX = x + fxr; int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
var currentColor = sourceOffsetRow[offsetX].ToVector4(); Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply();
if (fy < kernelXHeight) if (fy < kernelXHeight)
{ {
@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
float blue = MathF.Sqrt((bX * bX) + (bY * bY)); float blue = MathF.Sqrt((bX * bX) + (bY * bY));
ref TPixel pixel = ref targetRow[x]; ref TPixel pixel = ref targetRow[x];
pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply());
} }
}); });

4
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -113,13 +113,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
var currentColor = row[offsetX].ToVector4(); Vector4 currentColor = row[offsetX].ToVector4().Premultiply();
destination += kernel[fy, fx] * currentColor; destination += kernel[fy, fx] * currentColor;
} }
} }
ref TPixel pixel = ref targetRow[x]; ref TPixel pixel = ref targetRow[x];
pixel.PackFromVector4(destination); pixel.PackFromVector4(destination.UnPremultiply());
} }
}); });
} }

4
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX); offsetX = offsetX.Clamp(0, maxX);
var currentColor = sourceOffsetRow[offsetX].ToVector4(); Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply();
currentColor *= this.KernelXY[fy, fx]; currentColor *= this.KernelXY[fy, fx];
red += currentColor.X; red += currentColor.X;
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
} }
ref TPixel pixel = ref targetRow[x]; ref TPixel pixel = ref targetRow[x];
pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply());
} }
}); });

2
src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs

@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{ {
if (this.Grayscale) if (this.Grayscale)
{ {
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration); new GrayscaleBt709Processor<TPixel>(1F).Apply(source, sourceRectangle, configuration);
} }
} }
} }

2
src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs

@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{ {
if (this.Grayscale) if (this.Grayscale)
{ {
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration); new GrayscaleBt709Processor<TPixel>(1F).Apply(source, sourceRectangle, configuration);
} }
} }

2
src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs

@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{ {
if (this.Grayscale) if (this.Grayscale)
{ {
new GrayscaleBt709Processor<TPixel>().Apply(source, sourceRectangle, configuration); new GrayscaleBt709Processor<TPixel>(1F).Apply(source, sourceRectangle, configuration);
} }
} }

81
src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs

@ -1,81 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="IImageProcessor{TPixel}"/> to change the alpha component of an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AlphaProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AlphaProcessor{TPixel}"/> class.
/// </summary>
/// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 1.</param>
/// <exception cref="System.ArgumentException">
/// <paramref name="percent"/> is less than 0 or is greater than 1.
/// </exception>
public AlphaProcessor(float percent)
{
Guard.MustBeBetweenOrEqualTo(percent, 0, 1, nameof(percent));
this.Value = percent;
}
/// <summary>
/// Gets the alpha value.
/// </summary>
public float Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
var alphaVector = new Vector4(1, 1, 1, this.Value);
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);
for (int x = minX; x < maxX; x++)
{
ref TPixel pixel = ref row[x - startX];
pixel.PackFromVector4(pixel.ToVector4() * alphaVector);
}
});
}
}
}

87
src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs

@ -1,87 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="IImageProcessor{TPixel}"/> to change the brightness of an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BrightnessProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="BrightnessProcessor{TPixel}"/> class.
/// </summary>
/// <param name="brightness">The new brightness of the image. Must be between -100 and 100.</param>
/// <exception cref="System.ArgumentException">
/// <paramref name="brightness"/> is less than -100 or is greater than 100.
/// </exception>
public BrightnessProcessor(int brightness)
{
Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness));
this.Value = brightness;
}
/// <summary>
/// Gets the brightness value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
float brightness = this.Value / 100F;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);
for (int x = minX; x < maxX; x++)
{
ref TPixel pixel = ref row[x - startX];
// TODO: Check this with other formats.
Vector4 vector = pixel.ToVector4().Expand();
Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness);
vector = new Vector4(transformed, vector.W);
pixel.PackFromVector4(vector.Compress());
}
});
}
}
}

89
src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs

@ -1,89 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="IImageProcessor{TPixel}"/> to change the contrast of an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ContrastProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ContrastProcessor{TPixel}"/> class.
/// </summary>
/// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param>
/// <exception cref="System.ArgumentException">
/// <paramref name="contrast"/> is less than -100 or is greater than 100.
/// </exception>
public ContrastProcessor(int contrast)
{
Guard.MustBeBetweenOrEqualTo(contrast, -100, 100, nameof(contrast));
this.Value = contrast;
}
/// <summary>
/// Gets the contrast value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
float contrast = (100F + this.Value) / 100F;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
var contrastVector = new Vector4(contrast, contrast, contrast, 1);
var shiftVector = new Vector4(.5F, .5F, .5F, 1);
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);
for (int x = minX; x < maxX; x++)
{
ref TPixel pixel = ref row[x - startX];
Vector4 vector = pixel.ToVector4().Expand();
vector -= shiftVector;
vector *= contrastVector;
vector += shiftVector;
pixel.PackFromVector4(vector.Compress());
}
});
}
}
}

66
src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs

@ -1,66 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="IImageProcessor{TPixel}"/> to invert the colors of an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class InvertProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
Vector3 inverseVector = Vector3.One;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);
for (int x = minX; x < maxX; x++)
{
ref TPixel pixel = ref row[x - startX];
var vector = pixel.ToVector4();
Vector3 vector3 = inverseVector - new Vector3(vector.X, vector.Y, vector.Z);
pixel.PackFromVector4(new Vector4(vector3, vector.W));
}
});
}
}
}

23
src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a black and white filter matrix to the image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BlackWhiteProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="BlackWhiteProcessor{TPixel}"/> class.
/// </summary>
public BlackWhiteProcessor()
: base(MatrixFilters.BlackWhiteFilter)
{
}
}
}

34
src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a brightness filter matrix using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class BrightnessProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="BrightnessProcessor{TPixel}"/> class.
/// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
public BrightnessProcessor(float amount)
: base(MatrixFilters.CreateBrightnessFilter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

23
src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AchromatomalyProcessor{TPixel}"/> class.
/// </summary>
public AchromatomalyProcessor()
: base(MatrixFilters.AchromatomalyFilter)
{
}
}
}

23
src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatopsiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AchromatopsiaProcessor{TPixel}"/> class.
/// </summary>
public AchromatopsiaProcessor()
: base(MatrixFilters.AchromatopsiaFilter)
{
}
}
}

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DeuteranomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class DeuteranomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <summary>
public override Matrix4x4 Matrix => new Matrix4x4 /// Initializes a new instance of the <see cref="DeuteranomalyProcessor{TPixel}"/> class.
/// </summary>
public DeuteranomalyProcessor()
: base(MatrixFilters.DeuteranomalyFilter)
{ {
M11 = 0.8F, }
M12 = 0.258F,
M21 = 0.2F,
M22 = 0.742F,
M23 = 0.142F,
M33 = 0.858F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
} }
} }

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DeuteranopiaProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class DeuteranopiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <summary>
public override Matrix4x4 Matrix => new Matrix4x4 /// Initializes a new instance of the <see cref="DeuteranopiaProcessor{TPixel}"/> class.
/// </summary>
public DeuteranopiaProcessor()
: base(MatrixFilters.DeuteranopiaFilter)
{ {
M11 = 0.625F, }
M12 = 0.7F,
M21 = 0.375F,
M22 = 0.3F,
M23 = 0.3F,
M33 = 0.7F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
} }
} }

23
src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProtanomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ProtanomalyProcessor{TPixel}"/> class.
/// </summary>
public ProtanomalyProcessor()
: base(MatrixFilters.ProtanomalyFilter)
{
}
}
}

21
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs

@ -10,22 +10,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProtanopiaProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class ProtanopiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <summary>
public override Matrix4x4 Matrix => new Matrix4x4 /// Initializes a new instance of the <see cref="ProtanopiaProcessor{TPixel}"/> class.
/// </summary>
public ProtanopiaProcessor()
: base(MatrixFilters.ProtanopiaFilter)
{ {
M11 = 0.567F, }
M12 = 0.558F,
M21 = 0.433F,
M22 = 0.442F,
M23 = 0.242F,
M33 = 0.758F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
} }
} }

0
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/README.md → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class TritanomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class TritanomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <summary>
public override Matrix4x4 Matrix => new Matrix4x4 /// Initializes a new instance of the <see cref="TritanomalyProcessor{TPixel}"/> class.
/// </summary>
public TritanomalyProcessor()
: base(MatrixFilters.TritanomalyFilter)
{ {
M11 = 0.967F, }
M21 = 0.33F,
M22 = 0.733F,
M23 = 0.183F,
M32 = 0.267F,
M33 = 0.817F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
} }
} }

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class TritanopiaProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class TritanopiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <summary>
public override Matrix4x4 Matrix => new Matrix4x4 /// Initializes a new instance of the <see cref="TritanopiaProcessor{TPixel}"/> class.
/// </summary>
public TritanopiaProcessor()
: base(MatrixFilters.TritanopiaFilter)
{ {
M11 = 0.95F, }
M21 = 0.05F,
M22 = 0.433F,
M23 = 0.475F,
M32 = 0.567F,
M33 = 0.525F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
} }
} }

34
src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a contrast filter matrix using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ContrastProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ContrastProcessor{TPixel}"/> class.
/// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
public ContrastProcessor(float amount)
: base(MatrixFilters.CreateContrastFilter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

62
src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs

@ -0,0 +1,62 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Provides methods that accept a <see cref="Matrix4x4"/> matrix to apply freeform filters to images.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FilterProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="FilterProcessor{TPixel}"/> class.
/// </summary>
/// <param name="matrix">The matrix used to apply the image filter</param>
public FilterProcessor(Matrix4x4 matrix)
{
this.Matrix = matrix;
}
/// <summary>
/// Gets the <see cref="Matrix4x4"/> used to apply the image filter.
/// </summary>
public Matrix4x4 Matrix { get; }
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
int endX = interest.Right;
Matrix4x4 matrix = this.Matrix;
Parallel.For(
startY,
endY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y);
for (int x = startX; x < endX; x++)
{
ref TPixel pixel = ref row[x];
var vector = Vector4.Transform(pixel.ToVector4(), matrix);
pixel.PackFromVector4(vector);
}
});
}
}
}

30
src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GrayscaleBt601Processor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GrayscaleBt601Processor{TPixel}"/> class.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public GrayscaleBt601Processor(float amount)
: base(MatrixFilters.CreateGrayscaleBt601Filter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

30
src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GrayscaleBt709Processor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GrayscaleBt709Processor{TPixel}"/> class.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public GrayscaleBt709Processor(float amount)
: base(MatrixFilters.CreateGrayscaleBt709Filter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

29
src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a hue filter matrix using the given angle of rotation in degrees
/// </summary>
internal class HueProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="HueProcessor{TPixel}"/> class.
/// </summary>
/// <param name="degrees">The angle of rotation in degrees</param>
public HueProcessor(float degrees)
: base(MatrixFilters.CreateHueFilter(degrees))
{
this.Degrees = degrees;
}
/// <summary>
/// Gets the angle of rotation in degrees
/// </summary>
public float Degrees { get; }
}
}

30
src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a filter matrix that inverts the colors of an image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class InvertProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="InvertProcessor{TPixel}"/> class.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public InvertProcessor(float amount)
: base(MatrixFilters.CreateInvertFilter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

23
src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class KodachromeProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="KodachromeProcessor{TPixel}"/> class.
/// </summary>
public KodachromeProcessor()
: base(MatrixFilters.KodachromeFilter)
{
}
}
}

15
src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs → src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating an old Lomograph effect. /// Converts the colors of the image recreating an old Lomograph effect.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class LomographProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class LomographProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private static readonly TPixel VeryDarkGreen = ColorBuilder<TPixel>.FromRGBA(0, 10, 0, 255); private static readonly TPixel VeryDarkGreen = ColorBuilder<TPixel>.FromRGBA(0, 10, 0, 255);
@ -28,23 +28,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="options">The options effecting blending and composition.</param> /// <param name="options">The options effecting blending and composition.</param>
public LomographProcessor(MemoryManager memoryManager, GraphicsOptions options) public LomographProcessor(MemoryManager memoryManager, GraphicsOptions options)
: base(MatrixFilters.LomographFilter)
{ {
this.memoryManager = memoryManager; this.memoryManager = memoryManager;
this.options = options; this.options = options;
} }
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = 1.5F,
M22 = 1.45F,
M33 = 1.11F,
M41 = -.1F,
M42 = .0F,
M43 = -.08F,
M44 = 1
};
/// <inheritdoc/> /// <inheritdoc/>
protected override void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {

30
src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies an opacity filter matrix using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class OpacityProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="OpacityProcessor{TPixel}"/> class.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public OpacityProcessor(float amount)
: base(MatrixFilters.CreateOpacityFilter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

24
src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs → src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -13,11 +12,11 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating an old Polaroid effect. /// Converts the colors of the image recreating an old Polaroid effect.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class PolaroidProcessor<TPixel> : ColorMatrixProcessor<TPixel> internal class PolaroidProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private static readonly TPixel VeryDarkOrange = ColorBuilder<TPixel>.FromRGB(102, 34, 0); private static readonly TPixel VeryDarkOrange = ColorBuilder<TPixel>.FromRGB(102, 34, 0);
private static readonly TPixel LightOrange = ColorBuilder<TPixel>.FromRGBA(255, 153, 102, 178); private static readonly TPixel LightOrange = ColorBuilder<TPixel>.FromRGBA(255, 153, 102, 128);
private readonly MemoryManager memoryManager; private readonly MemoryManager memoryManager;
@ -29,29 +28,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="options">The options effecting blending and composition.</param> /// <param name="options">The options effecting blending and composition.</param>
public PolaroidProcessor(MemoryManager memoryManager, GraphicsOptions options) public PolaroidProcessor(MemoryManager memoryManager, GraphicsOptions options)
: base(MatrixFilters.PolaroidFilter)
{ {
this.memoryManager = memoryManager; this.memoryManager = memoryManager;
this.options = options; this.options = options;
} }
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = 1.538F,
M12 = -0.062F,
M13 = -0.262F,
M21 = -0.022F,
M22 = 1.578F,
M23 = -0.022F,
M31 = .216F,
M32 = -.16F,
M33 = 1.5831F,
M41 = 0.02F,
M42 = -0.05F,
M43 = -0.05F,
M44 = 1
};
/// <inheritdoc/> /// <inheritdoc/>
protected override void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void AfterApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {

34
src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a saturation filter matrix using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class SaturateProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="SaturateProcessor{TPixel}"/> class.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
public SaturateProcessor(float amount)
: base(MatrixFilters.CreateSaturateFilter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

30
src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs

@ -0,0 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Applies a sepia filter matrix using the given amount.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class SepiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="SepiaProcessor{TPixel}"/> class.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public SepiaProcessor(float amount)
: base(MatrixFilters.CreateSepiaFilter(amount))
{
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

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

Loading…
Cancel
Save