diff --git a/.gitignore b/.gitignore
index c2e6f7d536..4942818972 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,9 @@ bld/
# Visual Studo 2015 cache/options directory
.vs/
+# Jetbrains Rider cache/options directory
+.idea/
+
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
diff --git a/APACHE-2.0-LICENSE.txt b/APACHE-2.0-LICENSE.txt
deleted file mode 100644
index a666c6e078..0000000000
--- a/APACHE-2.0-LICENSE.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2012 James South
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
diff --git a/ImageSharp.v2.ncrunchsolution b/ImageSharp.v2.ncrunchsolution
deleted file mode 100644
index b98737f1c0..0000000000
--- a/ImageSharp.v2.ncrunchsolution
+++ /dev/null
@@ -1,14 +0,0 @@
-
- 1
- false
- false
- true
- UseDynamicAnalysis
- UseStaticAnalysis
- UseStaticAnalysis
- UseStaticAnalysis
- UseDynamicAnalysis
-
-
-
-
\ No newline at end of file
diff --git a/ImageSharp.v3.ncrunchsolution b/ImageSharp.v3.ncrunchsolution
deleted file mode 100644
index 10420ac91d..0000000000
--- a/ImageSharp.v3.ncrunchsolution
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- True
- True
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000..2eeb57968e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2018 Six Labors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/NuGet.config b/NuGet.config
index b2c967cc97..322105d4d4 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -1,6 +1,7 @@
+
diff --git a/config.wyam b/config.wyam
deleted file mode 100644
index 3a4b64c540..0000000000
--- a/config.wyam
+++ /dev/null
@@ -1,4 +0,0 @@
-#recipe Docs
-Settings[Keys.Host] = "imagesharp.org";
-Settings[Keys.Title] = "Image Sharp";
-FileSystem.OutputPath = "./docs";
\ No newline at end of file
diff --git a/packages.xml b/packages.xml
deleted file mode 100644
index c7ff4de4cd..0000000000
--- a/packages.xml
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index eb3c29dd98..3e320dccc7 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -36,9 +36,9 @@
-
-
-
+
+
+
All
diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
index 8bebb3de79..d6dade7703 100644
--- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
@@ -169,19 +169,5 @@ namespace SixLabors.ImageSharp
{
return (byte)value.Clamp(0, 255);
}
-
- ///
- /// Swaps the references to two objects in memory.
- ///
- /// The first reference.
- /// The second reference.
- /// The type of object.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Swap(ref T first, ref T second)
- {
- T temp = second;
- second = first;
- first = temp;
- }
}
}
diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
index 7cb193e828..8133ebb38e 100644
--- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
+++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
@@ -12,6 +12,34 @@ namespace SixLabors.ImageSharp
///
internal static class Vector4Extensions
{
+ ///
+ /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
+ ///
+ /// The to premultiply
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 Premultiply(this Vector4 source)
+ {
+ float w = source.W;
+ Vector4 premultiplied = source * w;
+ premultiplied.W = w;
+ return premultiplied;
+ }
+
+ ///
+ /// Reverses the result of premultiplying a vector via .
+ ///
+ /// The to premultiply
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 UnPremultiply(this Vector4 source)
+ {
+ float w = source.W;
+ Vector4 unpremultiplied = source / w;
+ unpremultiplied.W = w;
+ return unpremultiplied;
+ }
+
///
/// Compresses a linear color signal to its sRGB equivalent.
///
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index 7d781e77fe..75c9190d24 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/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);
}
- ///
- /// Gets the bounding from the given matrix.
- ///
- /// The source rectangle.
- /// The transformation matrix.
- ///
- /// The .
- ///
- 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);
- }
-
///
/// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one.
diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs
index 22bcc82e16..ac56d02778 100644
--- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs
+++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs
@@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp
if (!this.mutate && this.destination == null)
{
// This will only work if the first processor applied is the cloning one thus
- // realistically for this optermissation to work the resize must the first processor
- // applied any only up processors will take the douple data path.
+ // realistically for this optimization to work the resize must the first processor
+ // applied any only up processors will take the double data path.
if (processor is ICloningImageProcessor cloningImageProcessor)
{
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle);
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
index 510a097eaf..46bafcc0cf 100644
--- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
@@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
this.startingOffset = 0;
for (int i = 0; i < this.matrixWidth; i++)
{
- // Good to disable here as we are not comparing matematical output.
+ // Good to disable here as we are not comparing mathematical output.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (matrix[0, i] != 0)
{
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
// Calculate the error
Vector4 error = source.ToVector4() - transformed.ToVector4();
- // Loop through and distribute the error amongst neighbouring pixels.
+ // Loop through and distribute the error amongst neighboring pixels.
for (int row = 0; row < this.matrixHeight; row++)
{
int matrixY = y + row;
@@ -115,10 +115,8 @@ namespace SixLabors.ImageSharp.Dithering.Base
ref TPixel pixel = ref rowSpan[matrixX];
var offsetColor = pixel.ToVector4();
- var coefficientVector = new Vector4(coefficient);
- Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor;
- result.W = offsetColor.W;
+ Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor;
pixel.PackFromVector4(result);
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
index 8b53194fdc..78a9de6c45 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
@@ -23,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
///
- public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions
+ public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector
{
///
public Image Decode(Configuration configuration, Stream stream)
@@ -34,5 +32,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
return new BmpDecoderCore(configuration, this).Decode(stream);
}
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, "stream");
+
+ return new BmpDecoderCore(configuration, this).Identify(stream);
+ }
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 07b7fabb6c..b4db7527d0 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@@ -94,62 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Image Decode(Stream stream)
where TPixel : struct, IPixel
{
- this.currentStream = stream;
-
try
{
- this.ReadFileHeader();
- this.ReadInfoHeader();
-
- // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
- // If the height is negative, then this is a Windows bitmap whose origin
- // is the upper-left corner and not the lower-left.The inverted flag
- // indicates a lower-left origin.Our code will be outputting an
- // upper-left origin pixel array.
- bool inverted = false;
- if (this.infoHeader.Height < 0)
- {
- inverted = true;
- this.infoHeader.Height = -this.infoHeader.Height;
- }
-
- int colorMapSize = -1;
-
- if (this.infoHeader.ClrUsed == 0)
- {
- if (this.infoHeader.BitsPerPixel == 1 ||
- this.infoHeader.BitsPerPixel == 4 ||
- this.infoHeader.BitsPerPixel == 8)
- {
- colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
- }
- }
- else
- {
- colorMapSize = this.infoHeader.ClrUsed * 4;
- }
-
- byte[] palette = null;
-
- 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}'");
- }
+ this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor pixels = image.Lock())
@@ -192,6 +140,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public IImageInfo Identify(Stream stream)
+ {
+ this.ReadImageHeaders(stream, out _, out _);
+ return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData());
+ }
+
///
/// Returns the y- value based on the given height.
///
@@ -624,5 +582,73 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Offset = BitConverter.ToInt32(data, 10)
};
}
+
+ ///
+ /// Reads the and from the stream and sets the corresponding fields.
+ ///
+ private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
+ {
+ this.currentStream = stream;
+
+ try
+ {
+ this.ReadFileHeader();
+ this.ReadInfoHeader();
+
+ // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
+ // If the height is negative, then this is a Windows bitmap whose origin
+ // is the upper-left corner and not the lower-left.The inverted flag
+ // indicates a lower-left origin.Our code will be outputting an
+ // upper-left origin pixel array.
+ inverted = false;
+ if (this.infoHeader.Height < 0)
+ {
+ inverted = true;
+ this.infoHeader.Height = -this.infoHeader.Height;
+ }
+
+ int colorMapSize = -1;
+
+ if (this.infoHeader.ClrUsed == 0)
+ {
+ if (this.infoHeader.BitsPerPixel == 1 ||
+ this.infoHeader.BitsPerPixel == 4 ||
+ this.infoHeader.BitsPerPixel == 8)
+ {
+ colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
+ }
+ }
+ else
+ {
+ colorMapSize = this.infoHeader.ClrUsed * 4;
+ }
+
+ palette = null;
+
+ if (colorMapSize > 0)
+ {
+ // 256 * 4
+ if (colorMapSize > 1024)
+ {
+ throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
+ }
+
+ palette = new byte[colorMapSize];
+
+ this.currentStream.Read(palette, 0, colorMapSize);
+ }
+
+ if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(
+ $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
+ }
+ }
+ catch (IndexOutOfRangeException e)
+ {
+ throw new ImageFormatException("Bitmap does not have a valid format.", e);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs
index 11b5b57fa2..c81c51e8b4 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.PixelFormats;
@@ -12,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Decoder for generating an image out of a gif encoded stream.
///
- public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions
+ public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector
{
///
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@@ -33,8 +31,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
public Image Decode(Configuration configuration, Stream stream)
where TPixel : struct, IPixel
{
- var decoder = new GifDecoderCore(configuration, this);
- return decoder.Decode(stream);
+ var decoder = new GifDecoderCore(configuration, this);
+ return decoder.Decode(stream);
+ }
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, "stream");
+
+ var decoder = new GifDecoderCore(configuration, this);
+ return decoder.Identify(stream);
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index d70d8f29c2..0c1d8b21ac 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Performs the gif decoding operation.
///
- /// The pixel format.
- internal sealed class GifDecoderCore
- where TPixel : struct, IPixel
+ internal sealed class GifDecoderCore
{
///
/// The temp buffer used to reduce allocations.
@@ -46,11 +44,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private int globalColorTableLength;
- ///
- /// The previous frame.
- ///
- private ImageFrame previousFrame;
-
///
/// The area to restore.
///
@@ -72,12 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private ImageMetaData metaData;
///
- /// The image to decode the information to.
- ///
- private Image image;
-
- ///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The configuration.
/// The decoder options.
@@ -107,28 +95,85 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Decodes the stream to the image.
///
+ /// The pixel format.
/// The stream containing image data.
/// The decoded image
- public Image Decode(Stream stream)
+ public Image Decode(Stream stream)
+ where TPixel : struct, IPixel
{
+ Image image = null;
+ ImageFrame previousFrame = null;
try
{
- this.metaData = new ImageMetaData();
+ this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
- this.currentStream = stream;
+ // Loop though the respective gif parts and read the data.
+ int nextFlag = stream.ReadByte();
+ while (nextFlag != GifConstants.Terminator)
+ {
+ if (nextFlag == GifConstants.ImageLabel)
+ {
+ if (previousFrame != null && this.DecodingMode == FrameDecodingMode.First)
+ {
+ break;
+ }
- // Skip the identifier
- this.currentStream.Skip(6);
- this.ReadLogicalScreenDescriptor();
+ this.ReadFrame(ref image, ref previousFrame);
+ }
+ else if (nextFlag == GifConstants.ExtensionIntroducer)
+ {
+ int label = stream.ReadByte();
+ switch (label)
+ {
+ case GifConstants.GraphicControlLabel:
+ this.ReadGraphicalControlExtension();
+ break;
+ case GifConstants.CommentLabel:
+ this.ReadComments();
+ break;
+ case GifConstants.ApplicationExtensionLabel:
- if (this.logicalScreenDescriptor.GlobalColorTableFlag)
- {
- this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
+ // The application extension length should be 11 but we've got test images that incorrectly
+ // set this to 252.
+ int appLength = stream.ReadByte();
+ this.Skip(appLength); // No need to read.
+ break;
+ case GifConstants.PlainTextLabel:
+ int plainLength = stream.ReadByte();
+ this.Skip(plainLength); // Not supported by any known decoder.
+ break;
+ }
+ }
+ else if (nextFlag == GifConstants.EndIntroducer)
+ {
+ break;
+ }
this.globalColorTable = this.configuration.MemoryManager.Allocate(this.globalColorTableLength, true);
- // Read the global color table from the stream
- stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
+ nextFlag = stream.ReadByte();
+ if (nextFlag == -1)
+ {
+ break;
+ }
}
+ }
+ finally
+ {
+ this.globalColorTable?.Dispose();
+ }
+
+ return image;
+ }
+
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public IImageInfo Identify(Stream stream)
+ {
+ try
+ {
+ this.ReadLogicalScreenDescriptorAndGlobalColorTable(stream);
// Loop though the respective gif parts and read the data.
int nextFlag = stream.ReadByte();
@@ -136,12 +181,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
if (nextFlag == GifConstants.ImageLabel)
{
- if (this.previousFrame != null && this.DecodingMode == FrameDecodingMode.First)
- {
- break;
- }
-
- this.ReadFrame();
+ // Skip image block
+ this.Skip(0);
}
else if (nextFlag == GifConstants.ExtensionIntroducer)
{
@@ -149,7 +190,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (label)
{
case GifConstants.GraphicControlLabel:
- this.ReadGraphicalControlExtension();
+
+ // Skip graphic control extension block
+ this.Skip(0);
break;
case GifConstants.CommentLabel:
this.ReadComments();
@@ -184,7 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.globalColorTable?.Dispose();
}
- return this.image;
+ return new ImageInfo(new PixelTypeInfo(this.logicalScreenDescriptor.BitsPerPixel), this.logicalScreenDescriptor.Width, this.logicalScreenDescriptor.Height, this.metaData);
}
///
@@ -242,6 +285,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
Width = BitConverter.ToInt16(this.buffer, 0),
Height = BitConverter.ToInt16(this.buffer, 2),
+ BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1
BackgroundColorIndex = this.buffer[5],
PixelAspectRatio = this.buffer[6],
GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
@@ -302,7 +346,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Reads an individual gif frame.
///
- private void ReadFrame()
+ /// The pixel format.
+ /// The image to decode the information to.
+ /// The previous frame.
+ private void ReadFrame(ref Image image, ref ImageFrame previousFrame)
+ where TPixel : struct, IPixel
{
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
@@ -321,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
indices = this.configuration.MemoryManager.Allocate(imageDescriptor.Width * imageDescriptor.Height, true);
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
this.Skip(0);
@@ -351,10 +399,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Reads the frames colors, mapping indices to colors.
///
+ /// The pixel format.
+ /// The image to decode the information to.
+ /// The previous frame.
/// The indexed pixels.
/// The color table containing the available colors.
/// The
- private void ReadFrameColors(Span indices, Span colorTable, GifImageDescriptor descriptor)
+ private void ReadFrameColors(ref Image image, ref ImageFrame previousFrame, Span indices, Span colorTable, GifImageDescriptor descriptor)
+ where TPixel : struct, IPixel
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
@@ -365,30 +417,30 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageFrame imageFrame;
- if (this.previousFrame == null)
+ if (previousFrame == null)
{
// This initializes the image to become fully transparent because the alpha channel is zero.
- this.image = new Image(this.configuration, imageWidth, imageHeight, this.metaData);
+ image = new Image(this.configuration, imageWidth, imageHeight, this.metaData);
- this.SetFrameMetaData(this.image.Frames.RootFrame.MetaData);
+ this.SetFrameMetaData(image.Frames.RootFrame.MetaData);
- imageFrame = this.image.Frames.RootFrame;
+ imageFrame = image.Frames.RootFrame;
}
else
{
if (this.graphicsControlExtension != null &&
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);
imageFrame = currentFrame;
- this.RestoreToBackground(imageFrame);
+ this.RestoreToBackground(imageFrame, image.Width, image.Height);
}
int i = 0;
@@ -460,11 +512,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (prevFrame != null)
{
- this.previousFrame = prevFrame;
+ previousFrame = prevFrame;
return;
}
- this.previousFrame = currentFrame ?? this.image.Frames.RootFrame;
+ previousFrame = currentFrame ?? image.Frames.RootFrame;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@@ -476,8 +528,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Restores the current frame area to the background.
///
+ /// The pixel format.
/// The frame.
- private void RestoreToBackground(ImageFrame frame)
+ /// Width of the image.
+ /// Height of the image.
+ private void RestoreToBackground(ImageFrame frame, int imageWidth, int imageHeight)
+ where TPixel : struct, IPixel
{
if (this.restoreArea == null)
{
@@ -485,8 +541,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
// Optimization for when the size of the frame is the same as the image size.
- if (this.restoreArea.Value.Width == this.image.Width &&
- this.restoreArea.Value.Height == this.image.Height)
+ if (this.restoreArea.Value.Width == imageWidth &&
+ this.restoreArea.Value.Height == imageHeight)
{
using (PixelAccessor pixelAccessor = frame.Lock())
{
@@ -527,5 +583,29 @@ namespace SixLabors.ImageSharp.Formats.Gif
meta.DisposalMethod = this.graphicsControlExtension.DisposalMethod;
}
}
+
+ ///
+ /// Reads the logical screen descriptor and global color table blocks
+ ///
+ /// The stream containing image data.
+ private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
+ {
+ this.metaData = new ImageMetaData();
+
+ this.currentStream = stream;
+
+ // Skip the identifier
+ this.currentStream.Skip(6);
+ this.ReadLogicalScreenDescriptor();
+
+ if (this.logicalScreenDescriptor.GlobalColorTableFlag)
+ {
+ this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
+ this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength);
+
+ // Read the global color table from the stream
+ stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
index b1109c3e25..05f232a4be 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
@@ -22,6 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
public short Height { get; set; }
+ ///
+ /// Gets or sets the color depth, in number of bits per pixel.
+ ///
+ public int BitsPerPixel { get; set; }
+
///
/// Gets or sets the index at the Global Color Table for the Background Color.
/// The Background Color is the color used for those
diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs
index e392cf7c61..ffc40314d8 100644
--- a/src/ImageSharp/Formats/IImageDecoder.cs
+++ b/src/ImageSharp/Formats/IImageDecoder.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
diff --git a/src/ImageSharp/Formats/IImageInfoDetector.cs b/src/ImageSharp/Formats/IImageInfoDetector.cs
new file mode 100644
index 0000000000..b7769e8955
--- /dev/null
+++ b/src/ImageSharp/Formats/IImageInfoDetector.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.IO;
+
+namespace SixLabors.ImageSharp.Formats
+{
+ ///
+ /// Encapsulates methods used for detecting the raw image information without fully decoding it.
+ ///
+ public interface IImageInfoDetector
+ {
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The configuration for the image.
+ /// The containing image data.
+ /// The object
+ IImageInfo Identify(Configuration configuration, Stream stream);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
index 13be70e30b..ecebe9480d 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
///
/// Image decoder for generating an image out of a jpg stream.
///
- internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions
+ internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
///
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@@ -27,5 +27,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
return decoder.Decode(stream);
}
}
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, "stream");
+
+ using (var decoder = new OrigJpegDecoderCore(configuration, this))
+ {
+ return decoder.Identify(stream);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
index 2d34aee3b1..ddc294fa44 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
@@ -31,6 +31,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
///
public const int MaxTq = 3;
+ ///
+ /// The only supported precision
+ ///
+ public const int SupportedPrecision = 8;
+
// Complex value type field + mutable + available to other classes = the field MUST NOT be private :P
#pragma warning disable SA1401 // FieldsMustBePrivate
@@ -122,6 +127,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
IEnumerable IRawJpegData.Components => this.Components;
+ ///
+ /// Gets the color depth, in number of bits per pixel.
+ ///
+ public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
+
///
/// Gets the image height
///
@@ -173,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
public ImageMetaData MetaData { get; private set; }
///
- /// Decodes the image from the specified and sets
+ /// Decodes the image from the specified and sets
/// the data to image.
///
/// The pixel format.
@@ -187,6 +197,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
return this.PostProcessIntoImage();
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public IImageInfo Identify(Stream stream)
+ {
+ this.ParseStream(stream, true);
+
+ return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
+ }
+
///
public void Dispose()
{
@@ -622,7 +643,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.InputProcessor.ReadFull(this.Temp, 0, remaining);
// We only support 8-bit precision.
- if (this.Temp[0] != 8)
+ if (this.Temp[0] != SupportedPrecision)
{
throw new ImageFormatException("Only 8-Bit precision supported.");
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
index 68f525305b..91835b5d71 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
@@ -4,7 +4,6 @@
using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort;
-using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
@@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Image decoder for generating an image out of a jpg stream.
///
- public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions
+ public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
///
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@@ -30,5 +29,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return decoder.Decode(stream);
}
}
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, "stream");
+
+ using (var decoder = new OrigJpegDecoderCore(configuration, this))
+ {
+ return decoder.Identify(stream);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs
new file mode 100644
index 0000000000..ed21b91bfc
--- /dev/null
+++ b/src/ImageSharp/Formats/PixelTypeInfo.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats
+{
+ ///
+ /// Contains information about the pixels that make up an images visual data.
+ ///
+ public class PixelTypeInfo
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Color depth, in number of bits per pixel.
+ internal PixelTypeInfo(int bitsPerPixel)
+ {
+ this.BitsPerPixel = bitsPerPixel;
+ }
+
+ ///
+ /// Gets color depth, in number of bits per pixel.
+ ///
+ public int BitsPerPixel { get; }
+ }
+}
diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs
index 739fd6051e..9bde4f8cc3 100644
--- a/src/ImageSharp/Formats/Png/PngDecoder.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoder.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.PixelFormats;
@@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
///
///
- public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions
+ public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
{
///
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@@ -54,5 +52,12 @@ namespace SixLabors.ImageSharp.Formats.Png
var decoder = new PngDecoderCore(configuration, this);
return decoder.Decode(stream);
}
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ var decoder = new PngDecoderCore(configuration, this);
+ return decoder.Identify(stream);
+ }
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 2a5f5fabe2..6a04c77b96 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -14,7 +14,6 @@ using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
-using static SixLabors.ImageSharp.ComparableExtensions;
namespace SixLabors.ImageSharp.Formats.Png
{
@@ -237,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Png
deframeStream.AllocateNewBytes(currentChunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
- stream.Read(this.crcBuffer, 0, 4);
+ this.currentStream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length];
@@ -279,6 +278,66 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public IImageInfo Identify(Stream stream)
+ {
+ var metadata = new ImageMetaData();
+ this.currentStream = stream;
+ this.currentStream.Skip(8);
+ try
+ {
+ PngChunk currentChunk;
+ while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
+ {
+ try
+ {
+ switch (currentChunk.Type)
+ {
+ case PngChunkTypes.Header:
+ this.ReadHeaderChunk(currentChunk.Data);
+ this.ValidateHeader();
+ break;
+ case PngChunkTypes.Physical:
+ this.ReadPhysicalChunk(metadata, currentChunk.Data);
+ break;
+ case PngChunkTypes.Data:
+ this.SkipChunkDataAndCrc(currentChunk);
+ break;
+ case PngChunkTypes.Text:
+ this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length);
+ break;
+ case PngChunkTypes.End:
+ this.isEndChunkReached = true;
+ break;
+ }
+ }
+ finally
+ {
+ // Data is rented in ReadChunkData()
+ if (currentChunk.Data != null)
+ {
+ ArrayPool.Shared.Return(currentChunk.Data);
+ }
+ }
+ }
+ }
+ finally
+ {
+ this.scanline?.Dispose();
+ this.previousScanline?.Dispose();
+ }
+
+ if (this.header == null)
+ {
+ throw new ImageFormatException("PNG Image does not contain a header chunk");
+ }
+
+ return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata);
+ }
+
///
/// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits.
///
@@ -380,6 +439,28 @@ namespace SixLabors.ImageSharp.Formats.Png
this.scanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true);
}
+ ///
+ /// Calculates the correct number of bits per pixel for the given color type.
+ ///
+ /// The
+ private int CalculateBitsPerPixel()
+ {
+ switch (this.pngColorType)
+ {
+ case PngColorType.Grayscale:
+ case PngColorType.Palette:
+ return this.header.BitDepth;
+ case PngColorType.GrayscaleWithAlpha:
+ return this.header.BitDepth * 2;
+ case PngColorType.Rgb:
+ return this.header.BitDepth * 3;
+ case PngColorType.RgbWithAlpha:
+ return this.header.BitDepth * 4;
+ default:
+ throw new NotSupportedException("Unsupported PNG color type");
+ }
+ }
+
///
/// Calculates the correct number of bytes per pixel for the given color type.
///
@@ -508,7 +589,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.ProcessDefilteredScanline(this.scanline.Array, image);
- Swap(ref this.scanline, ref this.previousScanline);
+ this.SwapBuffers();
this.currentRow++;
}
}
@@ -584,7 +665,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Span rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
- Swap(ref this.scanline, ref this.previousScanline);
+ this.SwapBuffers();
this.currentRow += Adam7RowIncrement[this.pass];
}
@@ -1182,6 +1263,15 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
+ ///
+ /// Skips the chunk data and the cycle redundancy chunk read from the data.
+ ///
+ private void SkipChunkDataAndCrc(PngChunk chunk)
+ {
+ this.currentStream.Skip(chunk.Length);
+ this.currentStream.Skip(4);
+ }
+
///
/// Reads the chunk data from the stream.
///
@@ -1258,5 +1348,12 @@ namespace SixLabors.ImageSharp.Formats.Png
default: throw new ArgumentException($"Not a valid pass index: {passIndex}");
}
}
+
+ private void SwapBuffers()
+ {
+ Buffer temp = this.previousScanline;
+ this.previousScanline = this.scanline;
+ this.scanline = temp;
+ }
}
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs
index 1fd70a4d36..ee5651f2dd 100644
--- a/src/ImageSharp/Formats/Png/PngEncoder.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoder.cs
@@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public bool IgnoreMetadata { get; set; }
///
- /// Gets or sets the size of the color palette to use. Set to zero to leav png encoding to use pixel data.
+ /// Gets or sets the size of the color palette to use. Set to zero to leave png encoding to use pixel data.
///
public int PaletteSize { get; set; } = 0;
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 3250270404..e8e1726e9e 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -11,7 +11,6 @@ using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers;
-using static SixLabors.ImageSharp.ComparableExtensions;
namespace SixLabors.ImageSharp.Formats.Png
{
@@ -151,7 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Initializes a new instance of the class.
///
/// The to use for buffer allocations.
- /// The options for influancing the encoder
+ /// The options for influencing the encoder
public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options)
{
this.memoryManager = memoryManager;
@@ -643,7 +642,9 @@ namespace SixLabors.ImageSharp.Formats.Png
Buffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y);
deflateStream.Write(r.Array, 0, resultLength);
- Swap(ref this.rawScanline, ref this.previousScanline);
+ Buffer temp = this.rawScanline;
+ this.rawScanline = this.previousScanline;
+ this.previousScanline = temp;
}
}
diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs
new file mode 100644
index 0000000000..7355dc1fec
--- /dev/null
+++ b/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
+{
+ ///
+ /// Encapsulates the properties and methods that describe an image.
+ ///
+ public interface IImage : IImageInfo
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Image/IImageInfo.cs b/src/ImageSharp/Image/IImageInfo.cs
new file mode 100644
index 0000000000..25d5ec7cab
--- /dev/null
+++ b/src/ImageSharp/Image/IImageInfo.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.MetaData;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Encapsulates properties that descibe basic image information including dimensions, pixel type information
+ /// and additional metadata
+ ///
+ public interface IImageInfo
+ {
+ ///
+ /// Gets information about the image pixels.
+ ///
+ PixelTypeInfo PixelType { get; }
+
+ ///
+ /// Gets the width.
+ ///
+ int Width { get; }
+
+ ///
+ /// Gets the height.
+ ///
+ int Height { get; }
+
+ ///
+ /// Gets the metadata of the image.
+ ///
+ ImageMetaData MetaData { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs
index 69063a8de1..a2eacd3733 100644
--- a/src/ImageSharp/Image/Image.Decode.cs
+++ b/src/ImageSharp/Image/Image.Decode.cs
@@ -79,5 +79,19 @@ namespace SixLabors.ImageSharp
Image img = decoder.Decode(config, stream);
return (img, format);
}
+
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The stream.
+ /// the configuration.
+ ///
+ /// The or null if suitable info detector not found.
+ ///
+ private static IImageInfo InternalIdentity(Stream stream, Configuration config)
+ {
+ var detector = DiscoverDecoder(stream, config, out IImageFormat _) as IImageInfoDetector;
+ return detector?.Identify(config, stream);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs
index 90fa12e3f5..62668dd023 100644
--- a/src/ImageSharp/Image/Image.FromStream.cs
+++ b/src/ImageSharp/Image/Image.FromStream.cs
@@ -36,6 +36,37 @@ namespace SixLabors.ImageSharp
return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default));
}
+ ///
+ /// By reading the header on the provided stream this reads the raw image information.
+ ///
+ /// The image stream to read the header from.
+ ///
+ /// Thrown if the stream is not readable nor seekable.
+ ///
+ ///
+ /// The or null if suitable info detector not found.
+ ///
+ public static IImageInfo Identify(Stream stream)
+ {
+ return Identify(null, stream);
+ }
+
+ ///
+ /// Reads the raw image information from the specified stream without fully decoding it.
+ ///
+ /// The configuration.
+ /// The image stream to read the information from.
+ ///
+ /// Thrown if the stream is not readable nor seekable.
+ ///
+ ///
+ /// The or null if suitable info detector is not found.
+ ///
+ public static IImageInfo Identify(Configuration config, Stream stream)
+ {
+ return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default));
+ }
+
///
/// Create a new instance of the class from the given stream.
///
diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs
index 888630afe8..e96275a9fb 100644
--- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs
@@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
@@ -60,7 +61,16 @@ namespace SixLabors.ImageSharp
///
/// Initializes a new instance of the class.
///
- /// The to use for buffer allocations.
+ /// The of the frame.
+ /// The meta data.
+ internal ImageFrame(Size size, ImageFrameMetaData metaData)
+ : this(size.Width, size.Height, metaData)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
/// The source.
internal ImageFrame(MemoryManager memoryManager, ImageFrame source)
{
@@ -168,7 +178,9 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(pixelSource, nameof(pixelSource));
- ComparableExtensions.Swap(ref this.pixelBuffer, ref pixelSource.pixelBuffer);
+ Buffer2D temp = this.pixelBuffer;
+ this.pixelBuffer = pixelSource.pixelBuffer;
+ pixelSource.pixelBuffer = temp;
}
///
diff --git a/src/ImageSharp/Image/ImageInfo.cs b/src/ImageSharp/Image/ImageInfo.cs
new file mode 100644
index 0000000000..6f894cb599
--- /dev/null
+++ b/src/ImageSharp/Image/ImageInfo.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.MetaData;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Contains information about the image including dimensions, pixel type information and additional metadata
+ ///
+ internal sealed class ImageInfo : IImageInfo
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The image pixel type information.
+ /// The width of the image in pixels.
+ /// The height of the image in pixels.
+ /// The images metadata.
+ public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetaData metaData)
+ {
+ this.PixelType = pixelType;
+ this.Width = width;
+ this.Height = height;
+ this.MetaData = metaData;
+ }
+
+ ///
+ public PixelTypeInfo PixelType { get; }
+
+ ///
+ public int Width { get; }
+
+ ///
+ public int Height { get; }
+
+ ///
+ public ImageMetaData MetaData { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs
index 482971e540..be38b41f24 100644
--- a/src/ImageSharp/Image/Image{TPixel}.cs
+++ b/src/ImageSharp/Image/Image{TPixel}.cs
@@ -5,9 +5,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
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.
///
/// The pixel format.
- public sealed partial class Image : IDisposable, IConfigurable
+ public sealed partial class Image : IImage, IDisposable, IConfigurable
where TPixel : struct, IPixel
{
private Configuration configuration;
@@ -61,6 +61,7 @@ namespace SixLabors.ImageSharp
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata)
{
this.configuration = configuration ?? Configuration.Default;
+ this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection(this, width, height);
}
@@ -75,6 +76,7 @@ namespace SixLabors.ImageSharp
internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames)
{
this.configuration = configuration ?? Configuration.Default;
+ this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection(this, frames);
@@ -85,19 +87,16 @@ namespace SixLabors.ImageSharp
///
Configuration IConfigurable.Configuration => this.configuration;
- ///
- /// Gets the width.
- ///
+ ///
+ public PixelTypeInfo PixelType { get; }
+
+ ///
public int Width => this.frames.RootFrame.Width;
- ///
- /// Gets the height.
- ///
+ ///
public int Height => this.frames.RootFrame.Height;
- ///
- /// Gets the meta data of the image.
- ///
+ ///
public ImageMetaData MetaData { get; private set; } = new ImageMetaData();
///
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index 1d22e59cb2..b812b0b227 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -35,6 +35,7 @@
+
All
diff --git a/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs
index 0537ff514e..ae5f785a96 100644
--- a/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs
+++ b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs
@@ -290,9 +290,11 @@ namespace SixLabors.ImageSharp.PixelFormats
/// The
private static bool IsStandardNormalizedType(Type type)
{
- return type == typeof(Rgba32)
+ return
+ type == typeof(Alpha8)
|| type == typeof(Argb32)
- || type == typeof(Alpha8)
+ || type == typeof(Bgr24)
+ || type == typeof(Bgra32)
|| type == typeof(Bgr565)
|| type == typeof(Bgra4444)
|| type == typeof(Bgra5551)
@@ -300,8 +302,10 @@ namespace SixLabors.ImageSharp.PixelFormats
|| type == typeof(HalfVector2)
|| type == typeof(HalfVector4)
|| type == typeof(Rg32)
- || type == typeof(Rgba1010102)
- || type == typeof(Rgba64);
+ || type == typeof(Rgb24)
+ || type == typeof(Rgba32)
+ || type == typeof(Rgba64)
+ || type == typeof(Rgba1010102);
}
///
diff --git a/src/ImageSharp/PixelFormats/Rgb24.cs b/src/ImageSharp/PixelFormats/Rgb24.cs
index 5a12cff201..d867d9065e 100644
--- a/src/ImageSharp/PixelFormats/Rgb24.cs
+++ b/src/ImageSharp/PixelFormats/Rgb24.cs
@@ -126,5 +126,11 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.B = this.B;
dest.A = 255;
}
+
+ ///
+ public override string ToString()
+ {
+ return $"({this.R},{this.G},{this.B})";
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs
index 51647fc1f9..83a35f1895 100644
--- a/src/ImageSharp/PixelFormats/Rgba32.cs
+++ b/src/ImageSharp/PixelFormats/Rgba32.cs
@@ -364,7 +364,7 @@ namespace SixLabors.ImageSharp
/// A string representation of the packed vector.
public override string ToString()
{
- return this.ToVector4().ToString();
+ return $"({this.R},{this.G},{this.B},{this.A})";
}
///
diff --git a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs
index 300c073818..d64db34bad 100644
--- a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs
@@ -1,9 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
@@ -43,4 +41,4 @@ namespace SixLabors.ImageSharp
return source;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Effects/Brightness.cs b/src/ImageSharp/Processing/ColorMatrix/Brightness.cs
similarity index 62%
rename from src/ImageSharp/Processing/Effects/Brightness.cs
rename to src/ImageSharp/Processing/ColorMatrix/Brightness.cs
index 5c76287858..34b5347841 100644
--- a/src/ImageSharp/Processing/Effects/Brightness.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Brightness.cs
@@ -16,25 +16,33 @@ namespace SixLabors.ImageSharp
///
/// Alters the brightness component of the image.
///
+ ///
+ /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
+ ///
/// The pixel format.
/// The image this method extends.
- /// The new brightness of the image. Must be between -100 and 100.
+ /// The proportion of the conversion. Must be greater than or equal to 0.
/// The .
- public static IImageProcessingContext Brightness(this IImageProcessingContext source, int amount)
+ public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new BrightnessProcessor(amount));
///
/// Alters the brightness component of the image.
///
+ ///
+ /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
+ ///
/// The pixel format.
/// The image this method extends.
- /// The new brightness of the image. Must be between -100 and 100.
+ /// The proportion of the conversion. Must be greater than or equal to 0.
///
/// The structure that specifies the portion of the image object to alter.
///
/// The .
- public static IImageProcessingContext Brightness(this IImageProcessingContext source, int amount, Rectangle rectangle)
+ public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new BrightnessProcessor(amount), rectangle);
}
diff --git a/src/ImageSharp/Processing/Effects/Contrast.cs b/src/ImageSharp/Processing/ColorMatrix/Contrast.cs
similarity index 61%
rename from src/ImageSharp/Processing/Effects/Contrast.cs
rename to src/ImageSharp/Processing/ColorMatrix/Contrast.cs
index 562ab54dff..e0f388e287 100644
--- a/src/ImageSharp/Processing/Effects/Contrast.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Contrast.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
@@ -16,26 +15,34 @@ namespace SixLabors.ImageSharp
///
/// Alters the contrast component of the image.
///
+ ///
+ /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
+ ///
/// The pixel format.
/// The image this method extends.
- /// The new contrast of the image. Must be between -100 and 100.
+ /// The proportion of the conversion. Must be greater than or equal to 0.
/// The .
- public static IImageProcessingContext Contrast(this IImageProcessingContext source, int amount)
+ public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new ContrastProcessor(amount));
///
/// Alters the contrast component of the image.
///
+ ///
+ /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
+ ///
/// The pixel format.
/// The image this method extends.
- /// The new contrast of the image. Must be between -100 and 100.
+ /// The proportion of the conversion. Must be greater than or equal to 0.
///
/// The structure that specifies the portion of the image object to alter.
///
/// The .
- public static IImageProcessingContext Contrast(this IImageProcessingContext source, int amount, Rectangle rectangle)
+ public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new ContrastProcessor(amount), rectangle);
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs b/src/ImageSharp/Processing/ColorMatrix/Filter.cs
similarity index 60%
rename from src/ImageSharp/Processing/ColorMatrix/Saturation.cs
rename to src/ImageSharp/Processing/ColorMatrix/Filter.cs
index 26ca5ec204..7eb684978a 100644
--- a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Filter.cs
@@ -1,9 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
+using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
@@ -15,34 +14,34 @@ namespace SixLabors.ImageSharp
public static partial class ImageExtensions
{
///
- /// Alters the saturation component of the image.
+ /// Filters an image but the given color matrix
///
/// The pixel format.
/// The image this method extends.
- /// The new saturation of the image. Must be between -100 and 100.
+ /// The filter color matrix
/// The .
- public static IImageProcessingContext Saturation(this IImageProcessingContext source, int amount)
+ public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix)
where TPixel : struct, IPixel
{
- source.ApplyProcessor(new SaturationProcessor(amount));
+ source.ApplyProcessor(new FilterProcessor(matrix));
return source;
}
///
- /// Alters the saturation component of the image.
+ /// Filters an image but the given color matrix
///
/// The pixel format.
/// The image this method extends.
- /// The new saturation of the image. Must be between -100 and 100.
+ /// The filter color matrix
///
/// The structure that specifies the portion of the image object to alter.
///
/// The .
- public static IImageProcessingContext Saturation(this IImageProcessingContext source, int amount, Rectangle rectangle)
+ public static IImageProcessingContext Filter(this IImageProcessingContext source, Matrix4x4 matrix, Rectangle rectangle)
where TPixel : struct, IPixel
{
- source.ApplyProcessor(new SaturationProcessor(amount), rectangle);
+ source.ApplyProcessor(new FilterProcessor(matrix), rectangle);
return source;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs
index bcf48d3e2c..ee43d5b016 100644
--- a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs
@@ -21,12 +21,21 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Grayscale(this IImageProcessingContext source)
where TPixel : struct, IPixel
- {
- return Grayscale(source, GrayscaleMode.Bt709);
- }
+ => Grayscale(source, GrayscaleMode.Bt709);
///
- /// Applies Grayscale toning to the image.
+ /// Applies Grayscale toning to the image using the given amount.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The .
+ public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount)
+ where TPixel : struct, IPixel
+ => Grayscale(source, GrayscaleMode.Bt709, amount);
+
+ ///
+ /// Applies grayscale toning to the image with the given .
///
/// The pixel format.
/// The image this method extends.
@@ -34,10 +43,22 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode)
where TPixel : struct, IPixel
+ => Grayscale(source, mode, 1F);
+
+ ///
+ /// Applies grayscale toning to the image with the given using the given amount.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The formula to apply to perform the operation.
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The .
+ public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount)
+ where TPixel : struct, IPixel
{
IImageProcessor processor = mode == GrayscaleMode.Bt709
- ? (IImageProcessor)new GrayscaleBt709Processor()
- : new GrayscaleBt601Processor();
+ ? (IImageProcessor)new GrayscaleBt709Processor(amount)
+ : new GrayscaleBt601Processor(1F);
source.ApplyProcessor(processor);
return source;
@@ -54,9 +75,21 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle)
where TPixel : struct, IPixel
- {
- return Grayscale(source, GrayscaleMode.Bt709, rectangle);
- }
+ => Grayscale(source, 1F, rectangle);
+
+ ///
+ /// Applies Grayscale toning to the image using the given amount.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The proportion of the conversion. Must be between 0 and 1.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle)
+ where TPixel : struct, IPixel
+ => Grayscale(source, GrayscaleMode.Bt709, amount, rectangle);
///
/// Applies Grayscale toning to the image.
@@ -70,13 +103,28 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle)
where TPixel : struct, IPixel
+ => Grayscale(source, mode, 1F, rectangle);
+
+ ///
+ /// Applies Grayscale toning to the image using the given amount.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The formula to apply to perform the operation.
+ /// The proportion of the conversion. Must be between 0 and 1.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle)
+ where TPixel : struct, IPixel
{
IImageProcessor processor = mode == GrayscaleMode.Bt709
- ? (IImageProcessor)new GrayscaleBt709Processor()
- : new GrayscaleBt601Processor();
+ ? (IImageProcessor)new GrayscaleBt709Processor(amount)
+ : new GrayscaleBt601Processor(amount);
source.ApplyProcessor(processor, rectangle);
return source;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/ColorMatrix/Hue.cs b/src/ImageSharp/Processing/ColorMatrix/Hue.cs
index bfc931977d..76af10c369 100644
--- a/src/ImageSharp/Processing/ColorMatrix/Hue.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Hue.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp
///
/// The pixel format.
/// The image this method extends.
- /// The angle in degrees to adjust the image.
+ /// The rotation angle in degrees to adjust the hue.
/// The .
public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees)
where TPixel : struct, IPixel
@@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
///
/// The pixel format.
/// The image this method extends.
- /// The angle in degrees to adjust the image.
+ /// The rotation angle in degrees to adjust the hue.
///
/// The structure that specifies the portion of the image object to alter.
///
@@ -45,4 +45,4 @@ namespace SixLabors.ImageSharp
return source;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs b/src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs
new file mode 100644
index 0000000000..8cbc21b2a6
--- /dev/null
+++ b/src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs
@@ -0,0 +1,446 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+
+namespace SixLabors.ImageSharp.Processing
+{
+ ///
+ /// Provides extensions methods for the struct
+ ///
+ public static class MatrixFilters
+ {
+ ///
+ /// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness
+ ///
+ public static Matrix4x4 AchromatomalyFilter { get; } = new Matrix4x4
+ {
+ M11 = .618F,
+ M12 = .163F,
+ M13 = .163F,
+ M21 = .320F,
+ M22 = .775F,
+ M23 = .320F,
+ M31 = .062F,
+ M32 = .062F,
+ M33 = .516F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Achromatopsia (Monochrome) color blindness.
+ ///
+ public static Matrix4x4 AchromatopsiaFilter { get; } = new Matrix4x4
+ {
+ M11 = .299F,
+ M12 = .299F,
+ M13 = .299F,
+ M21 = .587F,
+ M22 = .587F,
+ M23 = .587F,
+ M31 = .114F,
+ M32 = .114F,
+ M33 = .114F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness.
+ ///
+ public static Matrix4x4 DeuteranomalyFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.8F,
+ M12 = 0.258F,
+ M21 = 0.2F,
+ M22 = 0.742F,
+ M23 = 0.142F,
+ M33 = 0.858F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Deuteranopia (Green-Blind) color blindness.
+ ///
+ public static Matrix4x4 DeuteranopiaFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.625F,
+ M12 = 0.7F,
+ M21 = 0.375F,
+ M22 = 0.3F,
+ M23 = 0.3F,
+ M33 = 0.7F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Protanomaly (Red-Weak) color blindness.
+ ///
+ public static Matrix4x4 ProtanomalyFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.817F,
+ M12 = 0.333F,
+ M21 = 0.183F,
+ M22 = 0.667F,
+ M23 = 0.125F,
+ M33 = 0.875F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Protanopia (Red-Blind) color blindness.
+ ///
+ public static Matrix4x4 ProtanopiaFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.567F,
+ M12 = 0.558F,
+ M21 = 0.433F,
+ M22 = 0.442F,
+ M23 = 0.242F,
+ M33 = 0.758F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness.
+ ///
+ public static Matrix4x4 TritanomalyFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.967F,
+ M21 = 0.33F,
+ M22 = 0.733F,
+ M23 = 0.183F,
+ M32 = 0.267F,
+ M33 = 0.817F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating Tritanopia (Blue-Blind) color blindness.
+ ///
+ public static Matrix4x4 TritanopiaFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.95F,
+ M21 = 0.05F,
+ M22 = 0.433F,
+ M23 = 0.475F,
+ M32 = 0.567F,
+ M33 = 0.525F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets an approximated black and white filter
+ ///
+ public static Matrix4x4 BlackWhiteFilter { get; } = new Matrix4x4()
+ {
+ M11 = 1.5F,
+ M12 = 1.5F,
+ M13 = 1.5F,
+ M21 = 1.5F,
+ M22 = 1.5F,
+ M23 = 1.5F,
+ M31 = 1.5F,
+ M32 = 1.5F,
+ M33 = 1.5F,
+ M41 = -1F,
+ M42 = -1F,
+ M43 = -1F,
+ M44 = 1
+ };
+
+ ///
+ /// Gets a filter recreating an old Kodachrome camera effect.
+ ///
+ public static Matrix4x4 KodachromeFilter { get; } = new Matrix4x4
+ {
+ M11 = 0.7297023F,
+ M22 = 0.6109577F,
+ M33 = 0.597218F,
+ M41 = 0.105F,
+ M42 = 0.145F,
+ M43 = 0.155F,
+ M44 = 1
+ }
+
+ * CreateSaturateFilter(1.2F) * CreateContrastFilter(1.35F);
+
+ ///
+ /// Gets a filter recreating an old Lomograph camera effect.
+ ///
+ public static Matrix4x4 LomographFilter { get; } = new Matrix4x4
+ {
+ M11 = 1.5F,
+ M22 = 1.45F,
+ M33 = 1.16F,
+ M41 = -.1F,
+ M42 = -.02F,
+ M43 = -.07F,
+ M44 = 1
+ }
+
+ * CreateSaturateFilter(1.1F) * CreateContrastFilter(1.33F);
+
+ ///
+ /// Gets a filter recreating an old Polaroid camera effect.
+ ///
+ public static Matrix4x4 PolaroidFilter { get; } = new Matrix4x4
+ {
+ M11 = 1.538F,
+ M12 = -0.062F,
+ M13 = -0.262F,
+ M21 = -0.022F,
+ M22 = 1.578F,
+ M23 = -0.022F,
+ M31 = .216F,
+ M32 = -.16F,
+ M33 = 1.5831F,
+ M41 = 0.02F,
+ M42 = -0.05F,
+ M43 = -0.05F,
+ M44 = 1
+ };
+
+ ///
+ /// Create a brightness filter matrix using the given amount.
+ ///
+ ///
+ /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
+ ///
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ /// The
+ public static Matrix4x4 CreateBrightnessFilter(float amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
+
+ // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ return new Matrix4x4
+ {
+ M11 = amount,
+ M22 = amount,
+ M33 = amount,
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create a contrast filter matrix using the given amount.
+ ///
+ ///
+ /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
+ ///
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ /// The
+ public static Matrix4x4 CreateContrastFilter(float amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
+
+ // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ float contrast = (-.5F * amount) + .5F;
+
+ return new Matrix4x4
+ {
+ M11 = amount,
+ M22 = amount,
+ M33 = amount,
+ M41 = contrast,
+ M42 = contrast,
+ M43 = contrast,
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.601.
+ ///
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The
+ public static Matrix4x4 CreateGrayscaleBt601Filter(float amount)
+ {
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+ amount = 1F - amount;
+
+ // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ return new Matrix4x4
+ {
+ M11 = .299F + (.701F * amount),
+ M12 = .299F - (.299F * amount),
+ M13 = .299F - (.299F * amount),
+ M21 = .587F - (.587F * amount),
+ M22 = .587F + (.413F * amount),
+ M23 = .587F - (.587F * amount),
+ M31 = .114F - (.114F * amount),
+ M32 = .114F - (.114F * amount),
+ M33 = .114F + (.886F * amount),
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.709.
+ ///
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The
+ public static Matrix4x4 CreateGrayscaleBt709Filter(float amount)
+ {
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+ amount = 1F - amount;
+
+ // https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ return new Matrix4x4
+ {
+ M11 = .2126F + (.7874F * amount),
+ M12 = .2126F - (.2126F * amount),
+ M13 = .2126F - (.2126F * amount),
+ M21 = .7152F - (.7152F * amount),
+ M22 = .7152F + (.2848F * amount),
+ M23 = .7152F - (.7152F * amount),
+ M31 = .0722F - (.0722F * amount),
+ M32 = .0722F - (.0722F * amount),
+ M33 = .0722F + (.9278F * amount),
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create a hue filter matrix using the given angle in degrees.
+ ///
+ /// The angle of rotation in degrees.
+ /// The
+ public static Matrix4x4 CreateHueFilter(float degrees)
+ {
+ // Wrap the angle round at 360.
+ degrees = degrees % 360;
+
+ // Make sure it's not negative.
+ while (degrees < 0)
+ {
+ degrees += 360;
+ }
+
+ float radian = MathFExtensions.DegreeToRadian(degrees);
+ float cosRadian = MathF.Cos(radian);
+ float sinRadian = MathF.Sin(radian);
+
+ // The matrix is set up to preserve the luminance of the image.
+ // See http://graficaobscura.com/matrix/index.html
+ // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
+ return new Matrix4x4
+ {
+ M11 = .213F + (cosRadian * .787F) - (sinRadian * .213F),
+ M12 = .213F - (cosRadian * .213F) - (sinRadian * 0.143F),
+ M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F),
+ M21 = .715F - (cosRadian * .715F) - (sinRadian * .715F),
+ M22 = .715F + (cosRadian * .285F) + (sinRadian * 0.140F),
+ M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F),
+ M31 = .072F - (cosRadian * .072F) + (sinRadian * .928F),
+ M32 = .072F - (cosRadian * .072F) - (sinRadian * 0.283F),
+ M33 = .072F + (cosRadian * .928F) + (sinRadian * .072F),
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create an invert filter matrix using the given amount.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The
+ public static Matrix4x4 CreateInvertFilter(float amount)
+ {
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+
+ // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ float invert = 1F - (2F * amount);
+
+ return new Matrix4x4
+ {
+ M11 = invert,
+ M22 = invert,
+ M33 = invert,
+ M41 = amount,
+ M42 = amount,
+ M43 = amount,
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create an opacity filter matrix using the given amount.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The
+ public static Matrix4x4 CreateOpacityFilter(float amount)
+ {
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+
+ // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ return new Matrix4x4
+ {
+ M11 = 1,
+ M22 = 1,
+ M33 = 1,
+ M44 = amount
+ };
+ }
+
+ ///
+ /// Create a saturation filter matrix using the given amount.
+ ///
+ ///
+ /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
+ ///
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ /// The
+ public static Matrix4x4 CreateSaturateFilter(float amount)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
+
+ // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ return new Matrix4x4
+ {
+ M11 = .213F + (.787F * amount),
+ M12 = .213F - (.213F * amount),
+ M13 = .213F - (.213F * amount),
+ M21 = .715F - (.715F * amount),
+ M22 = .715F + (.285F * amount),
+ M23 = .715F - (.715F * amount),
+ M31 = 1F - ((.213F + (.787F * amount)) + (.715F - (.715F * amount))),
+ M32 = 1F - ((.213F - (.213F * amount)) + (.715F + (.285F * amount))),
+ M33 = 1F - ((.213F - (.213F * amount)) + (.715F - (.715F * amount))),
+ M44 = 1
+ };
+ }
+
+ ///
+ /// Create a sepia filter matrix using the given amount.
+ /// The formula used matches the svg specification.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The
+ public static Matrix4x4 CreateSepiaFilter(float amount)
+ {
+ Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
+ amount = 1F - amount;
+
+ // See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
+ return new Matrix4x4
+ {
+ M11 = .393F + (.607F * amount),
+ M12 = .349F - (.349F * amount),
+ M13 = .272F - (.272F * amount),
+ M21 = .769F - (.769F * amount),
+ M22 = .686F + (.314F * amount),
+ M23 = .534F - (.534F * amount),
+ M31 = .189F - (.189F * amount),
+ M32 = .168F - (.168F * amount),
+ M33 = .131F + (.869F * amount),
+ M44 = 1
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Effects/Alpha.cs b/src/ImageSharp/Processing/ColorMatrix/Opacity.cs
similarity index 67%
rename from src/ImageSharp/Processing/Effects/Alpha.cs
rename to src/ImageSharp/Processing/ColorMatrix/Opacity.cs
index 2fac64e1cf..b310b4b915 100644
--- a/src/ImageSharp/Processing/Effects/Alpha.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Opacity.cs
@@ -18,24 +18,24 @@ namespace SixLabors.ImageSharp
///
/// The pixel format.
/// The image this method extends.
- /// The new opacity of the image. Must be between 0 and 1.
+ /// The proportion of the conversion. Must be between 0 and 1.
/// The .
- public static IImageProcessingContext Alpha(this IImageProcessingContext source, float percent)
+ public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount)
where TPixel : struct, IPixel
- => source.ApplyProcessor(new AlphaProcessor(percent));
+ => source.ApplyProcessor(new OpacityProcessor(amount));
///
/// Alters the alpha component of the image.
///
/// The pixel format.
/// The image this method extends.
- /// The new opacity of the image. Must be between 0 and 1.
+ /// The proportion of the conversion. Must be between 0 and 1.
///
/// The structure that specifies the portion of the image object to alter.
///
/// The .
- public static IImageProcessingContext Alpha(this IImageProcessingContext source, float percent, Rectangle rectangle)
+ public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel
- => source.ApplyProcessor(new AlphaProcessor(percent), rectangle);
+ => source.ApplyProcessor(new OpacityProcessor(amount), rectangle);
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturate.cs b/src/ImageSharp/Processing/ColorMatrix/Saturate.cs
new file mode 100644
index 0000000000..c7dd395aa3
--- /dev/null
+++ b/src/ImageSharp/Processing/ColorMatrix/Saturate.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Alters the saturation component of the image.
+ ///
+ ///
+ /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ /// The .
+ public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount)
+ where TPixel : struct, IPixel
+ {
+ source.ApplyProcessor(new SaturateProcessor(amount));
+ return source;
+ }
+
+ ///
+ /// Alters the saturation component of the image.
+ ///
+ ///
+ /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle)
+ where TPixel : struct, IPixel
+ {
+ source.ApplyProcessor(new SaturateProcessor(amount), rectangle);
+ return source;
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs
index d1116fac8b..0d686f4dba 100644
--- a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs
+++ b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs
@@ -1,9 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
@@ -22,7 +20,18 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Sepia(this IImageProcessingContext source)
where TPixel : struct, IPixel
- => source.ApplyProcessor(new SepiaProcessor());
+ => Sepia(source, 1F);
+
+ ///
+ /// Applies sepia toning to the image using the given amount.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The proportion of the conversion. Must be between 0 and 1.
+ /// The .
+ public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount)
+ where TPixel : struct, IPixel
+ => source.ApplyProcessor(new SepiaProcessor(amount));
///
/// Applies sepia toning to the image.
@@ -35,6 +44,20 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle)
where TPixel : struct, IPixel
- => source.ApplyProcessor(new SepiaProcessor(), rectangle);
+ => Sepia(source, 1F, rectangle);
+
+ ///
+ /// Applies sepia toning to the image.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The proportion of the conversion. Must be between 0 and 1.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ /// The .
+ public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle)
+ where TPixel : struct, IPixel
+ => source.ApplyProcessor(new SepiaProcessor(amount), rectangle);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Effects/Invert.cs b/src/ImageSharp/Processing/Effects/Invert.cs
index 9c0a7d3772..7dd9ed3dd7 100644
--- a/src/ImageSharp/Processing/Effects/Invert.cs
+++ b/src/ImageSharp/Processing/Effects/Invert.cs
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Invert(this IImageProcessingContext source)
where TPixel : struct, IPixel
- => source.ApplyProcessor(new InvertProcessor());
+ => source.ApplyProcessor(new InvertProcessor(1F));
///
/// Inverts the colors of the image.
@@ -34,6 +34,6 @@ namespace SixLabors.ImageSharp
/// The .
public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle)
where TPixel : struct, IPixel
- => source.ApplyProcessor(new InvertProcessor(), rectangle);
+ => source.ApplyProcessor(new InvertProcessor(1F), rectangle);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
index d736b91ee6..434ed02698 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
///
protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
- new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration);
+ new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
index 8907770e15..01cba15c4b 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
///
protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
- new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration);
+ new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
}
///
diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
index a2fd17c94b..a37d12f18c 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
///
protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
- new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration);
+ new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
}
///
diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
index fdee21ed6a..4672b2ad45 100644
--- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing
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();
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing
// we now need to move the pixel data/size data from one image base to another
if (cloned.Frames.Count != source.Frames.Count)
{
- throw new ImageProcessingException($"An error 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);
@@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing
}
///
- /// 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.
///
/// The source image. Cannot be null.
/// The source rectangle.
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs
deleted file mode 100644
index 9f81273433..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image to their black and white equivalent.
- ///
- /// The pixel format.
- internal class BlackWhiteProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4()
- {
- M11 = 1.5F,
- M12 = 1.5F,
- M13 = 1.5F,
- M21 = 1.5F,
- M22 = 1.5F,
- M23 = 1.5F,
- M31 = 1.5F,
- M32 = 1.5F,
- M33 = 1.5F,
- M41 = -1F,
- M42 = -1F,
- M43 = -1F,
- M44 = 1
- };
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
deleted file mode 100644
index 91e5c7f68f..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
- ///
- /// The pixel format.
- internal class AchromatomalyProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = .618F,
- M12 = .163F,
- M13 = .163F,
- M21 = .320F,
- M22 = .775F,
- M23 = .320F,
- M31 = .062F,
- M32 = .062F,
- M33 = .516F,
- M44 = 1
- };
-
- ///
- public override bool Compand => false;
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
deleted file mode 100644
index 0d6578852c..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
- ///
- /// The pixel format.
- internal class AchromatopsiaProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = .299F,
- M12 = .299F,
- M13 = .299F,
- M21 = .587F,
- M22 = .587F,
- M23 = .587F,
- M31 = .114F,
- M32 = .114F,
- M33 = .114F,
- M44 = 1
- };
-
- ///
- public override bool Compand => false;
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
deleted file mode 100644
index 4e32cb5298..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
- ///
- /// The pixel format.
- internal class ProtanomalyProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = 0.817F,
- M12 = 0.333F,
- M21 = 0.183F,
- M22 = 0.667F,
- M23 = 0.125F,
- M33 = 0.875F,
- M44 = 1
- };
-
- ///
- public override bool Compand => false;
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
deleted file mode 100644
index 4a64bfaa0d..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using System.Threading.Tasks;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.Primitives;
-
-namespace SixLabors.ImageSharp.Processing.Processors
-{
- ///
- /// The color matrix filter. Inherit from this class to perform operation involving color matrices.
- ///
- /// The pixel format.
- internal abstract class ColorMatrixProcessor : ImageProcessor, IColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public abstract Matrix4x4 Matrix { get; }
-
- ///
- public virtual bool Compand { get; set; } = true;
-
- ///
- protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- int startY = sourceRectangle.Y;
- int endY = sourceRectangle.Bottom;
- int startX = sourceRectangle.X;
- int endX = sourceRectangle.Right;
-
- // Align start/end positions.
- int minX = Math.Max(0, startX);
- int maxX = Math.Min(source.Width, endX);
- int minY = Math.Max(0, startY);
- int maxY = Math.Min(source.Height, endY);
-
- // Reset offset if necessary.
- if (minX > 0)
- {
- startX = 0;
- }
-
- if (minY > 0)
- {
- startY = 0;
- }
-
- Matrix4x4 matrix = this.Matrix;
- bool compand = this.Compand;
-
- Parallel.For(
- minY,
- maxY,
- configuration.ParallelOptions,
- y =>
- {
- Span 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);
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs
deleted file mode 100644
index 35dfe41a82..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.601
- /// .
- ///
- /// The pixel format.
- internal class GrayscaleBt601Processor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = .299F,
- M12 = .299F,
- M13 = .299F,
- M21 = .587F,
- M22 = .587F,
- M23 = .587F,
- M31 = .114F,
- M32 = .114F,
- M33 = .114F,
- M44 = 1
- };
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs
deleted file mode 100644
index 6bb460ee67..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709
- /// .
- ///
- /// The pixel format.
- internal class GrayscaleBt709Processor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = .2126F,
- M12 = .2126F,
- M13 = .2126F,
- M21 = .7152F,
- M22 = .7152F,
- M23 = .7152F,
- M31 = .0722F,
- M32 = .0722F,
- M33 = .0722F,
- M44 = 1
- };
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs
deleted file mode 100644
index adfdb6a788..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An to change the hue of an .
- ///
- /// The pixel format.
- internal class HueProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The new brightness of the image. Must be between -100 and 100.
- public HueProcessor(float angle)
- {
- // Wrap the angle round at 360.
- angle = angle % 360;
-
- // Make sure it's not negative.
- while (angle < 0)
- {
- angle += 360;
- }
-
- this.Angle = angle;
-
- float radians = 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;
- }
-
- ///
- /// Gets the rotation value.
- ///
- public float Angle { get; }
-
- ///
- public override Matrix4x4 Matrix { get; }
-
- ///
- public override bool Compand => false;
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs
deleted file mode 100644
index 84e7461b56..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Encapsulates properties and methods for creating processors that utilize a matrix to
- /// alter the image pixels.
- ///
- /// The pixel format.
- internal interface IColorMatrixProcessor : IImageProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Gets the used to alter the image.
- ///
- Matrix4x4 Matrix { get; }
-
- ///
- /// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing.
- ///
- bool Compand { get; set; }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs
deleted file mode 100644
index 4277e1fc2e..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image recreating an old Kodachrome camera effect.
- ///
- /// The pixel format.
- internal class KodachromeProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = 0.6997023F,
- M22 = 0.4609577F,
- M33 = 0.397218F,
- M41 = 0.005F,
- M42 = -0.005F,
- M43 = 0.005F,
- M44 = 1
- };
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs
deleted file mode 100644
index 1f01bc85dc..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An to change the saturation of an .
- ///
- /// The pixel format.
- internal class SaturationProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The new saturation of the image. Must be between -100 and 100.
- ///
- /// is less than -100 or is greater than 100.
- ///
- public SaturationProcessor(int saturation)
- {
- 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;
- }
-
- ///
- /// Gets the amount to apply.
- ///
- public int Amount { get; }
-
- ///
- public override Matrix4x4 Matrix { get; }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs
deleted file mode 100644
index d959ebf521..0000000000
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Converts the colors of the image to their sepia equivalent.
- /// The formula used matches the svg specification.
- ///
- /// The pixel format.
- internal class SepiaProcessor : ColorMatrixProcessor
- where TPixel : struct, IPixel
- {
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = .393F,
- M12 = .349F,
- M13 = .272F,
- M21 = .769F,
- M22 = .686F,
- M23 = .534F,
- M31 = .189F,
- M32 = .168F,
- M33 = .131F,
- M44 = 1
- };
-
- ///
- public override bool Compand => false;
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
index 2e2f5e3a6a..378978d63f 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
@@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
- var currentColor = sourceOffsetRow[offsetX].ToVector4();
+ Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply();
if (fy < kernelXHeight)
{
@@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
float blue = MathF.Sqrt((bX * bX) + (bY * bY));
ref TPixel pixel = ref targetRow[x];
- pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
+ pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply());
}
});
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
index e79a6cf27f..7730f1fa77 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
@@ -113,13 +113,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
- var currentColor = row[offsetX].ToVector4();
+ Vector4 currentColor = row[offsetX].ToVector4().Premultiply();
destination += kernel[fy, fx] * currentColor;
}
}
ref TPixel pixel = ref targetRow[x];
- pixel.PackFromVector4(destination);
+ pixel.PackFromVector4(destination.UnPremultiply());
}
});
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
index da64a970e2..b700343bf4 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
@@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
offsetX = offsetX.Clamp(0, maxX);
- var currentColor = sourceOffsetRow[offsetX].ToVector4();
+ Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply();
currentColor *= this.KernelXY[fy, fx];
red += currentColor.X;
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
ref TPixel pixel = ref targetRow[x];
- pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
+ pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply());
}
});
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
index f93787d129..741a6e308c 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
if (this.Grayscale)
{
- new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration);
+ new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
index 32c22a8ce9..0ffd7d48f5 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
@@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
if (this.Grayscale)
{
- new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration);
+ new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
index 3b98b77fc8..e5c5179716 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
@@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
if (this.Grayscale)
{
- new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration);
+ new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs
deleted file mode 100644
index 7e5bd02abc..0000000000
--- a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An to change the alpha component of an .
- ///
- /// The pixel format.
- internal class AlphaProcessor : ImageProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The percentage to adjust the opacity of the image. Must be between 0 and 1.
- ///
- /// is less than 0 or is greater than 1.
- ///
- public AlphaProcessor(float percent)
- {
- Guard.MustBeBetweenOrEqualTo(percent, 0, 1, nameof(percent));
- this.Value = percent;
- }
-
- ///
- /// Gets the alpha value.
- ///
- public float Value { get; }
-
- ///
- protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- int startY = sourceRectangle.Y;
- int endY = sourceRectangle.Bottom;
- int startX = sourceRectangle.X;
- int endX = sourceRectangle.Right;
-
- // Align start/end positions.
- int minX = Math.Max(0, startX);
- int maxX = Math.Min(source.Width, endX);
- int minY = Math.Max(0, startY);
- int maxY = Math.Min(source.Height, endY);
-
- // Reset offset if necessary.
- if (minX > 0)
- {
- startX = 0;
- }
-
- if (minY > 0)
- {
- startY = 0;
- }
-
- var alphaVector = new Vector4(1, 1, 1, this.Value);
-
- Parallel.For(
- minY,
- maxY,
- configuration.ParallelOptions,
- y =>
- {
- Span row = source.GetPixelRowSpan(y - startY);
-
- for (int x = minX; x < maxX; x++)
- {
- ref TPixel pixel = ref row[x - startX];
- pixel.PackFromVector4(pixel.ToVector4() * alphaVector);
- }
- });
- }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs
deleted file mode 100644
index c864330c9d..0000000000
--- a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An to change the brightness of an .
- ///
- /// The pixel format.
- internal class BrightnessProcessor : ImageProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The new brightness of the image. Must be between -100 and 100.
- ///
- /// is less than -100 or is greater than 100.
- ///
- public BrightnessProcessor(int brightness)
- {
- Guard.MustBeBetweenOrEqualTo(brightness, -100, 100, nameof(brightness));
- this.Value = brightness;
- }
-
- ///
- /// Gets the brightness value.
- ///
- public int Value { get; }
-
- ///
- protected override void OnApply(ImageFrame 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 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());
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs
deleted file mode 100644
index 5ab2662110..0000000000
--- a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An to change the contrast of an .
- ///
- /// The pixel format.
- internal class ContrastProcessor : ImageProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The new contrast of the image. Must be between -100 and 100.
- ///
- /// is less than -100 or is greater than 100.
- ///
- public ContrastProcessor(int contrast)
- {
- Guard.MustBeBetweenOrEqualTo(contrast, -100, 100, nameof(contrast));
- this.Value = contrast;
- }
-
- ///
- /// Gets the contrast value.
- ///
- public int Value { get; }
-
- ///
- protected override void OnApply(ImageFrame 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 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());
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs
deleted file mode 100644
index 448025f70a..0000000000
--- a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An to invert the colors of an .
- ///
- /// The pixel format.
- internal class InvertProcessor : ImageProcessor
- where TPixel : struct, IPixel
- {
- ///
- protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- int startY = sourceRectangle.Y;
- int endY = sourceRectangle.Bottom;
- int startX = sourceRectangle.X;
- int endX = sourceRectangle.Right;
- 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 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));
- }
- });
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs
new file mode 100644
index 0000000000..30fcfab4fd
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a black and white filter matrix to the image
+ ///
+ /// The pixel format.
+ internal class BlackWhiteProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BlackWhiteProcessor()
+ : base(MatrixFilters.BlackWhiteFilter)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs
new file mode 100644
index 0000000000..b1a68a9c91
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a brightness filter matrix using the given amount.
+ ///
+ /// The pixel format.
+ internal class BrightnessProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
+ ///
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ public BrightnessProcessor(float amount)
+ : base(MatrixFilters.CreateBrightnessFilter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs
new file mode 100644
index 0000000000..8d9bf98579
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
+ ///
+ /// The pixel format.
+ internal class AchromatomalyProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AchromatomalyProcessor()
+ : base(MatrixFilters.AchromatomalyFilter)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs
new file mode 100644
index 0000000000..f19c55933d
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
+ ///
+ /// The pixel format.
+ internal class AchromatopsiaProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AchromatopsiaProcessor()
+ : base(MatrixFilters.AchromatopsiaFilter)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs
similarity index 50%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs
index c4bb41ceb3..20a1d4ab46 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs
@@ -1,7 +1,6 @@
// 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
@@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
///
/// The pixel format.
- internal class DeuteranomalyProcessor : ColorMatrixProcessor
+ internal class DeuteranomalyProcessor : FilterProcessor
where TPixel : struct, IPixel
{
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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
- };
-
- ///
- public override bool Compand => false;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs
similarity index 51%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs
index 598af12ff0..e5e0225718 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs
@@ -1,7 +1,6 @@
// 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
@@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
///
/// The pixel format.
- internal class DeuteranopiaProcessor : ColorMatrixProcessor
+ internal class DeuteranopiaProcessor : FilterProcessor
where TPixel : struct, IPixel
{
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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
- };
-
- ///
- public override bool Compand => false;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs
new file mode 100644
index 0000000000..b7b61d5e59
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness.
+ ///
+ /// The pixel format.
+ internal class ProtanomalyProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ProtanomalyProcessor()
+ : base(MatrixFilters.ProtanomalyFilter)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs
similarity index 53%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs
index d49b4a2cc0..54753f5b57 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs
+++ b/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.
///
/// The pixel format.
- internal class ProtanopiaProcessor : ColorMatrixProcessor
+ internal class ProtanopiaProcessor : FilterProcessor
where TPixel : struct, IPixel
{
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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
- };
-
- ///
- public override bool Compand => false;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/README.md b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md
similarity index 100%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/README.md
rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs
similarity index 50%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs
index d34f22343c..57f4d4fa83 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs
@@ -1,7 +1,6 @@
// 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
@@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
///
/// The pixel format.
- internal class TritanomalyProcessor : ColorMatrixProcessor
+ internal class TritanomalyProcessor : FilterProcessor
where TPixel : struct, IPixel
{
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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
- };
-
- ///
- public override bool Compand => false;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs
similarity index 50%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs
index 453ac99a72..b03a18cf76 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs
@@ -1,7 +1,6 @@
// 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
@@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
///
/// The pixel format.
- internal class TritanopiaProcessor : ColorMatrixProcessor
+ internal class TritanopiaProcessor : FilterProcessor
where TPixel : struct, IPixel
{
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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
- };
-
- ///
- public override bool Compand => false;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs
new file mode 100644
index 0000000000..8ebeb939fb
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a contrast filter matrix using the given amount.
+ ///
+ /// The pixel format.
+ internal class ContrastProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
+ ///
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ public ContrastProcessor(float amount)
+ : base(MatrixFilters.CreateContrastFilter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
new file mode 100644
index 0000000000..30fe8c6b6f
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Helpers;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Provides methods that accept a matrix to apply freeform filters to images.
+ ///
+ /// The pixel format.
+ internal class FilterProcessor : ImageProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The matrix used to apply the image filter
+ public FilterProcessor(Matrix4x4 matrix)
+ {
+ this.Matrix = matrix;
+ }
+
+ ///
+ /// Gets the used to apply the image filter.
+ ///
+ public Matrix4x4 Matrix { get; }
+
+ ///
+ protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
+ {
+ var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
+ int startY = interest.Y;
+ int endY = interest.Bottom;
+ int startX = interest.X;
+ int endX = interest.Right;
+ Matrix4x4 matrix = this.Matrix;
+
+ Parallel.For(
+ startY,
+ endY,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span row = source.GetPixelRowSpan(y);
+
+ for (int x = startX; x < endX; x++)
+ {
+ ref TPixel pixel = ref row[x];
+ var vector = Vector4.Transform(pixel.ToVector4(), matrix);
+ pixel.PackFromVector4(vector);
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs
new file mode 100644
index 0000000000..7ea52dcb92
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601
+ ///
+ /// The pixel format.
+ internal class GrayscaleBt601Processor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ public GrayscaleBt601Processor(float amount)
+ : base(MatrixFilters.CreateGrayscaleBt601Filter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs
new file mode 100644
index 0000000000..2d97f65842
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709
+ ///
+ /// The pixel format.
+ internal class GrayscaleBt709Processor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ public GrayscaleBt709Processor(float amount)
+ : base(MatrixFilters.CreateGrayscaleBt709Filter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs
new file mode 100644
index 0000000000..302314db40
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a hue filter matrix using the given angle of rotation in degrees
+ ///
+ internal class HueProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The angle of rotation in degrees
+ public HueProcessor(float degrees)
+ : base(MatrixFilters.CreateHueFilter(degrees))
+ {
+ this.Degrees = degrees;
+ }
+
+ ///
+ /// Gets the angle of rotation in degrees
+ ///
+ public float Degrees { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs
new file mode 100644
index 0000000000..e258e9d96e
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a filter matrix that inverts the colors of an image
+ ///
+ /// The pixel format.
+ internal class InvertProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ public InvertProcessor(float amount)
+ : base(MatrixFilters.CreateInvertFilter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs
new file mode 100644
index 0000000000..6f27a04538
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image
+ ///
+ /// The pixel format.
+ internal class KodachromeProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public KodachromeProcessor()
+ : base(MatrixFilters.KodachromeFilter)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
similarity index 81%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
index f66ce80a59..33c4338d58 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs
+++ b/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.
///
/// The pixel format.
- internal class LomographProcessor : ColorMatrixProcessor
+ internal class LomographProcessor : FilterProcessor
where TPixel : struct, IPixel
{
private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255);
@@ -28,23 +28,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// The to use for buffer allocations.
/// The options effecting blending and composition.
public LomographProcessor(MemoryManager memoryManager, GraphicsOptions options)
+ : base(MatrixFilters.LomographFilter)
{
this.memoryManager = memoryManager;
this.options = options;
}
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = 1.5F,
- M22 = 1.45F,
- M33 = 1.11F,
- M41 = -.1F,
- M42 = .0F,
- M43 = -.08F,
- M44 = 1
- };
-
///
protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs
new file mode 100644
index 0000000000..1c0d2600ea
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies an opacity filter matrix using the given amount.
+ ///
+ /// The pixel format.
+ internal class OpacityProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ public OpacityProcessor(float amount)
+ : base(MatrixFilters.CreateOpacityFilter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
similarity index 75%
rename from src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
rename to src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
index a14919e863..f5b4b71920 100644
--- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -13,11 +12,11 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating an old Polaroid effect.
///
/// The pixel format.
- internal class PolaroidProcessor : ColorMatrixProcessor
+ internal class PolaroidProcessor : FilterProcessor
where TPixel : struct, IPixel
{
private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0);
- private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178);
+ private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128);
private readonly MemoryManager memoryManager;
@@ -29,29 +28,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// The to use for buffer allocations.
/// The options effecting blending and composition.
public PolaroidProcessor(MemoryManager memoryManager, GraphicsOptions options)
+ : base(MatrixFilters.PolaroidFilter)
{
this.memoryManager = memoryManager;
this.options = options;
}
- ///
- public override Matrix4x4 Matrix => new Matrix4x4
- {
- M11 = 1.538F,
- M12 = -0.062F,
- M13 = -0.262F,
- M21 = -0.022F,
- M22 = 1.578F,
- M23 = -0.022F,
- M31 = .216F,
- M32 = -.16F,
- M33 = 1.5831F,
- M41 = 0.02F,
- M42 = -0.05F,
- M43 = -0.05F,
- M44 = 1
- };
-
///
protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs
new file mode 100644
index 0000000000..44b3fe3ced
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a saturation filter matrix using the given amount.
+ ///
+ /// The pixel format.
+ internal class SaturateProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
+ /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
+ ///
+ /// The proportion of the conversion. Must be greater than or equal to 0.
+ public SaturateProcessor(float amount)
+ : base(MatrixFilters.CreateSaturateFilter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs
new file mode 100644
index 0000000000..b30d0fe052
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Applies a sepia filter matrix using the given amount.
+ ///
+ /// The pixel format.
+ internal class SepiaProcessor : FilterProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The proportion of the conversion. Must be between 0 and 1.
+ public SepiaProcessor(float amount)
+ : base(MatrixFilters.CreateSepiaFilter(amount))
+ {
+ this.Amount = amount;
+ }
+
+ ///
+ /// Gets the proportion of the conversion
+ ///
+ public float Amount { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
new file mode 100644
index 0000000000..8595e86922
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
@@ -0,0 +1,245 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Helpers;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Provides the base methods to perform affine transforms on an image.
+ ///
+ /// The pixel format.
+ internal class AffineTransformProcessor : InterpolatedTransformProcessorBase
+ where TPixel : struct, IPixel
+ {
+ private Size targetDimensions;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ public AffineTransformProcessor(Matrix3x2 matrix)
+ : this(matrix, KnownResamplers.Bicubic)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ /// The sampler to perform the transform operation.
+ public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler)
+ : this(matrix, sampler, Size.Empty)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ /// The sampler to perform the transform operation.
+ /// The target dimensions to constrain the transformed image to.
+ public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions)
+ : base(sampler)
+ {
+ // Transforms are inverted else the output is the opposite of the expected.
+ Matrix3x2.Invert(matrix, out matrix);
+ this.TransformMatrix = matrix;
+ this.targetDimensions = targetDimensions;
+ }
+
+ ///
+ /// Gets the matrix used to supply the affine transform
+ ///
+ public Matrix3x2 TransformMatrix { get; }
+
+ ///
+ protected override Image CreateDestination(Image source, Rectangle sourceRectangle)
+ {
+ if (this.targetDimensions == Size.Empty)
+ {
+ // TODO: CreateDestination() should not modify the processors state! (kinda CQRS)
+ this.targetDimensions = this.GetTransformedDimensions(sourceRectangle.Size, this.TransformMatrix);
+ }
+
+ // We will always be creating the clone even for mutate because we may need to resize the canvas
+ IEnumerable> frames =
+ source.Frames.Select(x => new ImageFrame(this.targetDimensions, x.MetaData.Clone()));
+
+ // Use the overload to prevent an extra frame being added
+ return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames);
+ }
+
+ ///
+ protected override void OnApply(
+ ImageFrame source,
+ ImageFrame destination,
+ Rectangle sourceRectangle,
+ Configuration configuration)
+ {
+ int height = this.targetDimensions.Height;
+ int width = this.targetDimensions.Width;
+
+ Rectangle sourceBounds = source.Bounds();
+ var targetBounds = new Rectangle(0, 0, width, height);
+
+ // Since could potentially be resizing the canvas we might need to re-calculate the matrix
+ Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds);
+
+ if (this.Sampler is NearestNeighborResampler)
+ {
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span destRow = destination.GetPixelRowSpan(y);
+
+ for (int x = 0; x < width; x++)
+ {
+ var point = Point.Transform(new Point(x, y), matrix);
+ if (sourceBounds.Contains(point.X, point.Y))
+ {
+ destRow[x] = source[point.X, point.Y];
+ }
+ }
+ });
+
+ return;
+ }
+
+ int maxSourceX = source.Width - 1;
+ int maxSourceY = source.Height - 1;
+ (float radius, float scale, float ratio) xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width);
+ (float radius, float scale, float ratio) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height);
+ float xScale = xRadiusScale.scale;
+ float yScale = yRadiusScale.scale;
+ var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius);
+ IResampler sampler = this.Sampler;
+ var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY);
+ int xLength = (int)MathF.Ceiling((radius.X * 2) + 2);
+ int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2);
+
+ using (var yBuffer = new Buffer2D(yLength, height))
+ using (var xBuffer = new Buffer2D(xLength, height))
+ {
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span destRow = destination.GetPixelRowSpan(y);
+ Span ySpan = yBuffer.GetRowSpan(y);
+ Span xSpan = xBuffer.GetRowSpan(y);
+
+ for (int x = 0; x < width; x++)
+ {
+ // Use the single precision position to calculate correct bounding pixels
+ // otherwise we get rogue pixels outside of the bounds.
+ var point = Vector2.Transform(new Vector2(x, y), matrix);
+
+ // Clamp sampling pixel radial extents to the source image edges
+ Vector2 maxXY = point + radius;
+ Vector2 minXY = point - radius;
+
+ // max, maxY, minX, minY
+ var extents = new Vector4(
+ MathF.Floor(maxXY.X + .5F),
+ MathF.Floor(maxXY.Y + .5F),
+ MathF.Ceiling(minXY.X - .5F),
+ MathF.Ceiling(minXY.Y - .5F));
+
+ int right = (int)extents.X;
+ int bottom = (int)extents.Y;
+ int left = (int)extents.Z;
+ int top = (int)extents.W;
+
+ extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);
+
+ int maxX = (int)extents.X;
+ int maxY = (int)extents.Y;
+ int minX = (int)extents.Z;
+ int minY = (int)extents.W;
+
+ if (minX == maxX || minY == maxY)
+ {
+ continue;
+ }
+
+ // It appears these have to be calculated on-the-fly.
+ // Precalulating transformed weights would require prior knowledge of every transformed pixel location
+ // since they can be at sub-pixel positions on both axis.
+ // I've optimized where I can but am always open to suggestions.
+ if (yScale > 1 && xScale > 1)
+ {
+ CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan);
+ CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan);
+ }
+ else
+ {
+ CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan);
+ CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan);
+ }
+
+ // Now multiply the results against the offsets
+ Vector4 sum = Vector4.Zero;
+ for (int yy = 0, j = minY; j <= maxY; j++, yy++)
+ {
+ float yWeight = ySpan[yy];
+
+ for (int xx = 0, i = minX; i <= maxX; i++, xx++)
+ {
+ float xWeight = xSpan[xx];
+ var vector = source[i, j].ToVector4();
+
+ // Values are first premultiplied to prevent darkening of edge pixels
+ Vector4 mupltiplied = vector.Premultiply();
+ sum += mupltiplied * xWeight * yWeight;
+ }
+ }
+
+ ref TPixel dest = ref destRow[x];
+
+ // Reverse the premultiplication
+ dest.PackFromVector4(sum.UnPremultiply());
+ }
+ });
+ }
+ }
+
+ ///
+ /// Gets a transform matrix adjusted for final processing based upon the target image bounds.
+ ///
+ /// The source image bounds.
+ /// The destination image bounds.
+ ///
+ /// The .
+ ///
+ protected virtual Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle)
+ {
+ return this.TransformMatrix;
+ }
+
+ ///
+ /// Gets the bounding relative to the source for the given transformation matrix.
+ ///
+ /// The source rectangle.
+ /// The transformation matrix.
+ /// The
+ protected virtual Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix)
+ {
+ return sourceDimensions;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
index ab93e0e384..c39311bc33 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
@@ -15,13 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal class AutoOrientProcessor : ImageProcessor
where TPixel : struct, IPixel
{
- ///
- /// Initializes a new instance of the class.
- ///
- public AutoOrientProcessor()
- {
- }
-
+ ///
protected override void BeforeImageApply(Image source, Rectangle sourceRectangle)
{
Orientation orientation = GetExifOrientation(source);
@@ -33,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
break;
case Orientation.BottomRight:
- new RotateProcessor() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle);
+ new RotateProcessor((int)RotateType.Rotate180).Apply(source, sourceRectangle);
break;
case Orientation.BottomLeft:
@@ -41,21 +35,21 @@ namespace SixLabors.ImageSharp.Processing.Processors
break;
case Orientation.LeftTop:
- new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
+ new RotateProcessor((int)RotateType.Rotate90).Apply(source, sourceRectangle);
new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle);
break;
case Orientation.RightTop:
- new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
+ new RotateProcessor((int)RotateType.Rotate90).Apply(source, sourceRectangle);
break;
case Orientation.RightBottom:
new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle);
- new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
+ new RotateProcessor((int)RotateType.Rotate270).Apply(source, sourceRectangle);
break;
case Orientation.LeftBottom:
- new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
+ new RotateProcessor((int)RotateType.Rotate270).Apply(source, sourceRectangle);
break;
case Orientation.Unknown:
diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs
new file mode 100644
index 0000000000..34a0866615
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Numerics;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// A base class that provides methods to allow the automatic centering of affine transforms
+ ///
+ /// The pixel format.
+ internal abstract class CenteredAffineTransformProcessor : AffineTransformProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ /// The sampler to perform the transform operation.
+ protected CenteredAffineTransformProcessor(Matrix3x2 matrix, IResampler sampler)
+ : base(matrix, sampler)
+ {
+ }
+
+ ///
+ protected override Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle)
+ {
+ var translationToTargetCenter = Matrix3x2.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F);
+ var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F);
+ return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter;
+ }
+
+ ///
+ protected override Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix)
+ {
+ var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height);
+
+ if (!Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix))
+ {
+ // TODO: Shouldn't we throw an exception instead?
+ return sourceDimensions;
+ }
+
+ return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix).Size;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs
new file mode 100644
index 0000000000..dc2dd28ab1
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Numerics;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// A base class that provides methods to allow the automatic centering of non-affine transforms
+ ///
+ /// The pixel format.
+ internal abstract class CenteredProjectiveTransformProcessor : ProjectiveTransformProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ /// The sampler to perform the transform operation.
+ protected CenteredProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler)
+ : base(matrix, sampler)
+ {
+ }
+
+ ///
+ protected override Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle)
+ {
+ var translationToTargetCenter = Matrix4x4.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F, 0);
+ var translateToSourceCenter = Matrix4x4.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F, 0);
+ return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter;
+ }
+
+ ///
+ protected override Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix4x4 matrix)
+ {
+ return Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 sizeMatrix)
+ ? TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix)
+ : sourceRectangle;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
index 5568745864..9448aac5e0 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
@@ -60,5 +60,9 @@ namespace SixLabors.ImageSharp.Processing.Processors
source.SwapPixelsBuffers(targetPixels);
}
}
+
+ ///
+ protected override void AfterImageApply(Image source, Rectangle sourceRectangle)
+ => TransformHelpers.UpdateDimensionalMetData(source);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs
new file mode 100644
index 0000000000..27f9a1ace6
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs
@@ -0,0 +1,117 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// The base class for performing interpolated affine and non-affine transforms.
+ ///
+ /// The pixel format.
+ internal abstract class InterpolatedTransformProcessorBase : TransformProcessorBase
+ where TPixel : struct, IPixel
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The sampler to perform the transform operation.
+ protected InterpolatedTransformProcessorBase(IResampler sampler)
+ {
+ this.Sampler = sampler;
+ }
+
+ ///
+ /// Gets the sampler to perform interpolation of the transform operation.
+ ///
+ public IResampler Sampler { get; }
+
+ ///
+ /// Calculated the weights for the given point.
+ /// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered.
+ /// Additionally the weights are nomalized.
+ ///
+ /// The minimum sampling offset
+ /// The maximum sampling offset
+ /// The minimum source bounds
+ /// The maximum source bounds
+ /// The transformed point dimension
+ /// The sampler
+ /// The transformed image scale relative to the source
+ /// The collection of weights
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected static void CalculateWeightsDown(int min, int max, int sourceMin, int sourceMax, float point, IResampler sampler, float scale, Span weights)
+ {
+ float sum = 0;
+ ref float weightsBaseRef = ref weights[0];
+
+ // Downsampling weights requires more edge sampling plus normalization of the weights
+ for (int x = 0, i = min; i <= max; i++, x++)
+ {
+ int index = i;
+ if (index < sourceMin)
+ {
+ index = sourceMin;
+ }
+
+ if (index > sourceMax)
+ {
+ index = sourceMax;
+ }
+
+ float weight = sampler.GetValue((index - point) / scale);
+ sum += weight;
+ Unsafe.Add(ref weightsBaseRef, x) = weight;
+ }
+
+ if (sum > 0)
+ {
+ for (int i = 0; i < weights.Length; i++)
+ {
+ ref float wRef = ref Unsafe.Add(ref weightsBaseRef, i);
+ wRef = wRef / sum;
+ }
+ }
+ }
+
+ ///
+ /// Calculated the weights for the given point.
+ ///
+ /// The minimum source bounds
+ /// The maximum source bounds
+ /// The transformed point dimension
+ /// The sampler
+ /// The collection of weights
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected static void CalculateWeightsScaleUp(int sourceMin, int sourceMax, float point, IResampler sampler, Span weights)
+ {
+ ref float weightsBaseRef = ref weights[0];
+ for (int x = 0, i = sourceMin; i <= sourceMax; i++, x++)
+ {
+ float weight = sampler.GetValue(i - point);
+ Unsafe.Add(ref weightsBaseRef, x) = weight;
+ }
+ }
+
+ ///
+ /// Calculates the sampling radius for the current sampler
+ ///
+ /// The source dimension size
+ /// The destination dimension size
+ /// The radius, and scaling factor
+ protected (float radius, float scale, float ratio) GetSamplingRadius(int sourceSize, int destinationSize)
+ {
+ float ratio = (float)sourceSize / destinationSize;
+ float scale = ratio;
+
+ if (scale < 1F)
+ {
+ scale = 1F;
+ }
+
+ return (MathF.Ceiling(scale * this.Sampler.Radius), scale, ratio);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs
deleted file mode 100644
index 4a15254ab2..0000000000
--- a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Numerics;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.Primitives;
-
-namespace SixLabors.ImageSharp.Processing.Processors
-{
- ///
- /// Provides methods to transform an image using a .
- ///
- /// The pixel format.
- internal abstract class Matrix3x2Processor : ImageProcessor
- where TPixel : struct, IPixel
- {
- ///
- /// Gets the rectangle designating the target canvas.
- ///
- protected Rectangle CanvasRectangle { get; private set; }
-
- ///
- /// Creates a new target canvas to contain the results of the matrix transform.
- ///
- /// The source rectangle.
- /// The processing matrix.
- protected void CreateNewCanvas(Rectangle sourceRectangle, Matrix3x2 processMatrix)
- {
- Matrix3x2 sizeMatrix;
- this.CanvasRectangle = Matrix3x2.Invert(processMatrix, out sizeMatrix)
- ? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
- : sourceRectangle;
- }
-
- ///
- /// Gets a transform matrix adjusted to center upon the target image bounds.
- ///
- /// The source image.
- /// The transform matrix.
- ///
- /// The .
- ///
- protected Matrix3x2 GetCenteredMatrix(ImageFrame source, Matrix3x2 matrix)
- {
- var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F);
- var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F);
- return (translationToTargetCenter * matrix) * translateToSourceCenter;
- }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
new file mode 100644
index 0000000000..7e547727e6
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
@@ -0,0 +1,240 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Helpers;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Provides the base methods to perform non-affine transforms on an image.
+ /// TODO: Doesn't work yet! Implement tests + Finish implementation + Document Matrix4x4 behavior
+ ///
+ /// The pixel format.
+ internal class ProjectiveTransformProcessor : InterpolatedTransformProcessorBase
+ where TPixel : struct, IPixel
+ {
+ // TODO: We should use a Size instead! (See AffineTransformProcessor)
+ private Rectangle targetRectangle;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ public ProjectiveTransformProcessor(Matrix4x4 matrix)
+ : this(matrix, KnownResamplers.Bicubic)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ /// The sampler to perform the transform operation.
+ public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler)
+ : this(matrix, sampler, Rectangle.Empty)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The transform matrix
+ /// The sampler to perform the transform operation.
+ /// The rectangle to constrain the transformed image to.
+ public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Rectangle rectangle)
+ : base(sampler)
+ {
+ // Transforms are inverted else the output is the opposite of the expected.
+ Matrix4x4.Invert(matrix, out matrix);
+ this.TransformMatrix = matrix;
+ this.targetRectangle = rectangle;
+ }
+
+ ///
+ /// Gets the matrix used to supply the non-affine transform
+ ///
+ public Matrix4x4 TransformMatrix { get; }
+
+ ///
+ protected override Image CreateDestination(Image source, Rectangle sourceRectangle)
+ {
+ if (this.targetRectangle == Rectangle.Empty)
+ {
+ this.targetRectangle = this.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix);
+ }
+
+ // We will always be creating the clone even for mutate because we may need to resize the canvas
+ IEnumerable> frames =
+ source.Frames.Select(x => new ImageFrame(this.targetRectangle.Width, this.targetRectangle.Height, x.MetaData.Clone()));
+
+ // Use the overload to prevent an extra frame being added
+ return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames);
+ }
+
+ ///
+ protected override void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration)
+ {
+ int height = this.targetRectangle.Height;
+ int width = this.targetRectangle.Width;
+ Rectangle sourceBounds = source.Bounds();
+
+ // Since could potentially be resizing the canvas we might need to re-calculate the matrix
+ Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, this.targetRectangle);
+
+ if (this.Sampler is NearestNeighborResampler)
+ {
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span destRow = destination.GetPixelRowSpan(y);
+
+ for (int x = 0; x < width; x++)
+ {
+ var point = Point.Round(Vector2.Transform(new Vector2(x, y), matrix));
+ if (sourceBounds.Contains(point.X, point.Y))
+ {
+ destRow[x] = source[point.X, point.Y];
+ }
+ }
+ });
+
+ return;
+ }
+
+ int maxSourceX = source.Width - 1;
+ int maxSourceY = source.Height - 1;
+ (float radius, float scale, float ratio) xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width);
+ (float radius, float scale, float ratio) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height);
+ float xScale = xRadiusScale.scale;
+ float yScale = yRadiusScale.scale;
+ var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius);
+ IResampler sampler = this.Sampler;
+ var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY);
+ int xLength = (int)MathF.Ceiling((radius.X * 2) + 2);
+ int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2);
+
+ using (var yBuffer = new Buffer2D(yLength, height))
+ using (var xBuffer = new Buffer2D(xLength, height))
+ {
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span destRow = destination.GetPixelRowSpan(y);
+ Span ySpan = yBuffer.GetRowSpan(y);
+ Span xSpan = xBuffer.GetRowSpan(y);
+
+ for (int x = 0; x < width; x++)
+ {
+ // Use the single precision position to calculate correct bounding pixels
+ // otherwise we get rogue pixels outside of the bounds.
+ var point = Vector2.Transform(new Vector2(x, y), matrix);
+
+ // Clamp sampling pixel radial extents to the source image edges
+ Vector2 maxXY = point + radius;
+ Vector2 minXY = point - radius;
+
+ // max, maxY, minX, minY
+ var extents = new Vector4(
+ MathF.Floor(maxXY.X + .5F),
+ MathF.Floor(maxXY.Y + .5F),
+ MathF.Ceiling(minXY.X - .5F),
+ MathF.Ceiling(minXY.Y - .5F));
+
+ int right = (int)extents.X;
+ int bottom = (int)extents.Y;
+ int left = (int)extents.Z;
+ int top = (int)extents.W;
+
+ extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);
+
+ int maxX = (int)extents.X;
+ int maxY = (int)extents.Y;
+ int minX = (int)extents.Z;
+ int minY = (int)extents.W;
+
+ if (minX == maxX || minY == maxY)
+ {
+ continue;
+ }
+
+ // It appears these have to be calculated on-the-fly.
+ // Precalulating transformed weights would require prior knowledge of every transformed pixel location
+ // since they can be at sub-pixel positions on both axis.
+ // I've optimized where I can but am always open to suggestions.
+ if (yScale > 1 && xScale > 1)
+ {
+ CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan);
+ CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan);
+ }
+ else
+ {
+ CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan);
+ CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan);
+ }
+
+ // Now multiply the results against the offsets
+ Vector4 sum = Vector4.Zero;
+ for (int yy = 0, j = minY; j <= maxY; j++, yy++)
+ {
+ float yWeight = ySpan[yy];
+
+ for (int xx = 0, i = minX; i <= maxX; i++, xx++)
+ {
+ float xWeight = xSpan[xx];
+ var vector = source[i, j].ToVector4();
+
+ // Values are first premultiplied to prevent darkening of edge pixels
+ Vector4 mupltiplied = vector.Premultiply();
+ sum += mupltiplied * xWeight * yWeight;
+ }
+ }
+
+ ref TPixel dest = ref destRow[x];
+
+ // Reverse the premultiplication
+ dest.PackFromVector4(sum.UnPremultiply());
+ }
+ });
+ }
+ }
+
+ ///
+ /// Gets a transform matrix adjusted for final processing based upon the target image bounds.
+ ///
+ /// The source image bounds.
+ /// The destination image bounds.
+ ///
+ /// The .
+ ///
+ protected virtual Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle)
+ {
+ return this.TransformMatrix;
+ }
+
+ ///
+ /// Gets the bounding relative to the source for the given transformation matrix.
+ ///
+ /// The source rectangle.
+ /// The transformation matrix.
+ /// The
+ protected virtual Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix4x4 matrix)
+ {
+ return sourceRectangle;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
deleted file mode 100644
index 95fc5ee6d6..0000000000
--- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Memory;
-
-namespace SixLabors.ImageSharp.Processing.Processors
-{
- ///
- /// Conains the definition of and .
- ///
- internal abstract partial class ResamplingWeightedProcessor
- {
- ///
- /// Points to a collection of weights allocated in .
- ///
- internal struct WeightsWindow
- {
- ///
- /// The local left index position
- ///
- public int Left;
-
- ///
- /// The length of the weights window
- ///
- public int Length;
-
- ///
- /// The index in the destination buffer
- ///
- private readonly int flatStartIndex;
-
- ///
- /// The buffer containing the weights values.
- ///
- private readonly Buffer buffer;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The destination index in the buffer
- /// The local left index
- /// The span
- /// The length of the window
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal WeightsWindow(int index, int left, Buffer2D buffer, int length)
- {
- this.flatStartIndex = (index * buffer.Width) + left;
- this.Left = left;
- this.buffer = buffer.Buffer;
- this.Length = length;
- }
-
- ///
- /// Gets a reference to the first item of the window.
- ///
- /// The reference to the first item of the window
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref float GetStartReference()
- {
- return ref this.buffer[this.flatStartIndex];
- }
-
- ///
- /// Gets the span representing the portion of the that this window covers
- ///
- /// The
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span GetWindowSpan() => this.buffer.Span.Slice(this.flatStartIndex, this.Length);
-
- ///
- /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance.
- ///
- /// The input span of vectors
- /// The source row position.
- /// The weighted sum
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX)
- {
- ref float horizontalValues = ref this.GetStartReference();
- int left = this.Left;
- ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX);
-
- // Destination color components
- Vector4 result = Vector4.Zero;
-
- for (int i = 0; i < this.Length; i++)
- {
- float weight = Unsafe.Add(ref horizontalValues, i);
- Vector4 v = Unsafe.Add(ref vecPtr, i);
- result += v * weight;
- }
-
- return result;
- }
-
- ///
- /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance.
- /// Applies to all input vectors.
- ///
- /// The input span of vectors
- /// The source row position.
- /// The weighted sum
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX)
- {
- ref float horizontalValues = ref this.GetStartReference();
- int left = this.Left;
- ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX);
-
- // Destination color components
- Vector4 result = Vector4.Zero;
-
- for (int i = 0; i < this.Length; i++)
- {
- float weight = Unsafe.Add(ref horizontalValues, i);
- Vector4 v = Unsafe.Add(ref vecPtr, i);
- result += v.Expand() * weight;
- }
-
- return result;
- }
-
- ///
- /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x',
- /// weighted by weight values, pointed by this instance.
- ///
- /// The buffer of input vectors in row first order
- /// The row position
- /// The source column position.
- /// The weighted sum
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY)
- {
- ref float verticalValues = ref this.GetStartReference();
- int left = this.Left;
-
- // Destination color components
- Vector4 result = Vector4.Zero;
-
- for (int i = 0; i < this.Length; i++)
- {
- float yw = Unsafe.Add(ref verticalValues, i);
- int index = left + i + sourceY;
- result += firstPassPixels[x, index] * yw;
- }
-
- return result;
- }
- }
-
- ///
- /// Holds the values in an optimized contiguous memory region.
- ///
- internal class WeightsBuffer : IDisposable
- {
- private readonly Buffer2D dataBuffer;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The to use for buffer allocations.
- /// The size of the source window
- /// The size of the destination window
- public WeightsBuffer(MemoryManager memoryManager, int sourceSize, int destinationSize)
- {
- this.dataBuffer = memoryManager.Allocate2D(sourceSize, destinationSize, true);
- this.Weights = new WeightsWindow[destinationSize];
- }
-
- ///
- /// Gets the calculated values.
- ///
- public WeightsWindow[] Weights { get; }
-
- ///
- /// Disposes instance releasing it's backing buffer.
- ///
- public void Dispose()
- {
- this.dataBuffer.Dispose();
- }
-
- ///
- /// Slices a weights value at the given positions.
- ///
- /// The index in destination buffer
- /// The local left index value
- /// The local right index value
- /// The weights
- public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx)
- {
- return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1);
- }
- }
- }
-}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
index 3b7ff4b642..8c851d7d8f 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Adapted from
///
/// The pixel format.
- internal abstract partial class ResamplingWeightedProcessor : CloningImageProcessor
+ internal abstract class ResamplingWeightedProcessor : TransformProcessorBase
where TPixel : struct, IPixel
{
///
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
index 2c0f3f1540..e8463a2667 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
@@ -8,6 +8,7 @@ using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -68,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
///
- protected override unsafe void OnApply(ImageFrame source, ImageFrame cloned, Rectangle sourceRectangle, Configuration configuration)
+ protected override void OnApply(ImageFrame source, ImageFrame cloned, Rectangle sourceRectangle, Configuration configuration)
{
// Jump out, we'll deal with that later.
if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle)
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
index ddacf219b5..b93c869152 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
@@ -2,11 +2,9 @@
// 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.Memory;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -17,87 +15,55 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Provides methods that allow the rotating of images.
///
/// The pixel format.
- internal class RotateProcessor : Matrix3x2Processor
+ internal class RotateProcessor : CenteredAffineTransformProcessor
where TPixel : struct, IPixel
{
///
- /// The transform matrix to apply.
+ /// Initializes a new instance of the class.
///
- private Matrix3x2 processMatrix;
+ /// The angle of rotation in degrees.
+ public RotateProcessor(float degrees)
+ : this(degrees, KnownResamplers.Bicubic)
+ {
+ }
///
- /// Gets or sets the angle of processMatrix in degrees.
+ /// Initializes a new instance of the class.
///
- public float Angle { get; set; }
+ /// The angle of rotation in degrees.
+ /// The sampler to perform the rotating operation.
+ public RotateProcessor(float degrees, IResampler sampler)
+ : base(Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty), sampler)
+ {
+ this.Degrees = degrees;
+ }
///
- /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image.
+ /// Gets the angle of rotation in degrees.
///
- public bool Expand { get; set; } = true;
-
- ///
- protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- if (this.OptimizedApply(source, configuration))
- {
- return;
- }
-
- int height = this.CanvasRectangle.Height;
- int width = this.CanvasRectangle.Width;
- Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
- Rectangle sourceBounds = source.Bounds();
-
- using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height))
- {
- Parallel.For(
- 0,
- height,
- configuration.ParallelOptions,
- y =>
- {
- Span targetRow = targetPixels.GetRowSpan(y);
-
- for (int x = 0; x < width; x++)
- {
- var transformedPoint = Point.Rotate(new Point(x, y), matrix);
-
- if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
- {
- targetRow[x] = source[transformedPoint.X, transformedPoint.Y];
- }
- }
- });
-
- source.SwapPixelsBuffers(targetPixels);
- }
- }
+ public float Degrees { get; }
///
- protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
+ protected override void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration)
{
- if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon)
+ if (this.OptimizedApply(source, destination, configuration))
{
return;
}
- this.processMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, new Point(0, 0));
- if (this.Expand)
- {
- this.CreateNewCanvas(sourceRectangle, this.processMatrix);
- }
+ base.OnApply(source, destination, sourceRectangle, configuration);
}
///
- protected override void AfterImageApply(Image source, Rectangle sourceRectangle)
+ protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle)
{
- ExifProfile profile = source.MetaData.ExifProfile;
+ ExifProfile profile = destination.MetaData.ExifProfile;
if (profile == null)
{
return;
}
- if (MathF.Abs(this.Angle) < Constants.Epsilon)
+ if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon)
{
// No need to do anything so return.
return;
@@ -105,44 +71,62 @@ namespace SixLabors.ImageSharp.Processing.Processors
profile.RemoveValue(ExifTag.Orientation);
- if (this.Expand && profile.GetValue(ExifTag.PixelXDimension) != null)
+ base.AfterImageApply(source, destination, sourceRectangle);
+ }
+
+ ///
+ /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range
+ ///
+ /// The angle of rotation in degrees.
+ /// The
+ private static float WrapDegrees(float degrees)
+ {
+ degrees = degrees % 360;
+
+ while (degrees < 0)
{
- profile.SetValue(ExifTag.PixelXDimension, source.Width);
- profile.SetValue(ExifTag.PixelYDimension, source.Height);
+ degrees += 360;
}
+
+ return degrees;
}
///
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
///
/// The source image.
+ /// The destination image.
/// The configuration.
///
/// The
///
- private bool OptimizedApply(ImageFrame source, Configuration configuration)
+ private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration)
{
- if (MathF.Abs(this.Angle) < Constants.Epsilon)
+ // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible.
+ float degrees = WrapDegrees(this.Degrees);
+
+ if (MathF.Abs(degrees) < Constants.Epsilon)
{
- // No need to do anything so return.
+ // The destination will be blank here so copy all the pixel data over
+ source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
return true;
}
- if (MathF.Abs(this.Angle - 90) < Constants.Epsilon)
+ if (MathF.Abs(degrees - 90) < Constants.Epsilon)
{
- this.Rotate90(source, configuration);
+ this.Rotate90(source, destination, configuration);
return true;
}
- if (MathF.Abs(this.Angle - 180) < Constants.Epsilon)
+ if (MathF.Abs(degrees - 180) < Constants.Epsilon)
{
- this.Rotate180(source, configuration);
+ this.Rotate180(source, destination, configuration);
return true;
}
- if (MathF.Abs(this.Angle - 270) < Constants.Epsilon)
+ if (MathF.Abs(degrees - 270) < Constants.Epsilon)
{
- this.Rotate270(source, configuration);
+ this.Rotate270(source, destination, configuration);
return true;
}
@@ -153,95 +137,90 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the image 270 degrees clockwise at the centre point.
///
/// The source image.
+ /// The destination image.
/// The configuration.
- private void Rotate270(ImageFrame source, Configuration configuration)
+ private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
+ Rectangle destinationBounds = destination.Bounds();
- using (var targetPixels = new PixelAccessor(configuration.MemoryManager, height, width))
- {
- using (PixelAccessor sourcePixels = source.Lock())
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
{
- Parallel.For(
- 0,
- height,
- configuration.ParallelOptions,
- y =>
+ Span sourceRow = source.GetPixelRowSpan(y);
+ for (int x = 0; x < width; x++)
+ {
+ int newX = height - y - 1;
+ newX = height - newX - 1;
+ int newY = width - x - 1;
+
+ if (destinationBounds.Contains(newX, newY))
{
- for (int x = 0; x < width; x++)
- {
- int newX = height - y - 1;
- newX = height - newX - 1;
- int newY = width - x - 1;
- targetPixels[newX, newY] = sourcePixels[x, y];
- }
- });
- }
-
- source.SwapPixelsBuffers(targetPixels);
- }
+ destination[newX, newY] = sourceRow[x];
+ }
+ }
+ });
}
///
/// Rotates the image 180 degrees clockwise at the centre point.
///
/// The source image.
+ /// The destination image.
/// The configuration.
- private void Rotate180(ImageFrame source, Configuration configuration)
+ private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
- using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height))
- {
- Parallel.For(
- 0,
- height,
- configuration.ParallelOptions,
- y =>
- {
- Span sourceRow = source.GetPixelRowSpan(y);
- Span targetRow = targetPixels.GetRowSpan(height - y - 1);
-
- for (int x = 0; x < width; x++)
- {
- targetRow[width - x - 1] = sourceRow[x];
- }
- });
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span sourceRow = source.GetPixelRowSpan(y);
+ Span targetRow = destination.GetPixelRowSpan(height - y - 1);
- source.SwapPixelsBuffers(targetPixels);
- }
+ for (int x = 0; x < width; x++)
+ {
+ targetRow[width - x - 1] = sourceRow[x];
+ }
+ });
}
///
/// Rotates the image 90 degrees clockwise at the centre point.
///
/// The source image.
+ /// The destination image.
/// The configuration.
- private void Rotate90(ImageFrame source, Configuration configuration)
+ private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration)
{
int width = source.Width;
int height = source.Height;
+ Rectangle destinationBounds = destination.Bounds();
- using (var targetPixels = new PixelAccessor(configuration.MemoryManager, height, width))
- {
- Parallel.For(
- 0,
- height,
- configuration.ParallelOptions,
- y =>
+ Parallel.For(
+ 0,
+ height,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span sourceRow = source.GetPixelRowSpan(y);
+ int newX = height - y - 1;
+ for (int x = 0; x < width; x++)
{
- Span sourceRow = source.GetPixelRowSpan(y);
- int newX = height - y - 1;
- for (int x = 0; x < width; x++)
+ if (destinationBounds.Contains(newX, x))
{
- targetPixels[newX, x] = sourceRow[x];
+ destination[newX, x] = sourceRow[x];
}
- });
-
- source.SwapPixelsBuffers(targetPixels);
- }
+ }
+ });
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
index 9f6f1d17d2..8c47b35274 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
@@ -1,12 +1,6 @@
// 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.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -16,70 +10,40 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Provides methods that allow the skewing of images.
///
/// The pixel format.
- internal class SkewProcessor : Matrix3x2Processor
+ internal class SkewProcessor : CenteredAffineTransformProcessor
where TPixel : struct, IPixel
{
///
- /// The transform matrix to apply.
+ /// Initializes a new instance of the class.
///
- private Matrix3x2 processMatrix;
+ /// The angle in degrees to perform the skew along the x-axis.
+ /// The angle in degrees to perform the skew along the y-axis.
+ public SkewProcessor(float degreesX, float degreesY)
+ : this(degreesX, degreesY, KnownResamplers.Bicubic)
+ {
+ }
///
- /// Gets or sets the angle of rotation along the x-axis in degrees.
+ /// Initializes a new instance of the class.
///
- public float AngleX { get; set; }
+ /// The angle in degrees to perform the skew along the x-axis.
+ /// The angle in degrees to perform the skew along the y-axis.
+ /// The sampler to perform the skew operation.
+ public SkewProcessor(float degreesX, float degreesY, IResampler sampler)
+ : base(Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty), sampler)
+ {
+ this.DegreesX = degreesX;
+ this.DegreesY = degreesY;
+ }
///
- /// Gets or sets the angle of rotation along the y-axis in degrees.
+ /// Gets the angle of rotation along the x-axis in degrees.
///
- public float AngleY { get; set; }
+ public float DegreesX { get; }
///
- /// Gets or sets a value indicating whether to expand the canvas to fit the skewed image.
+ /// Gets the angle of rotation along the y-axis in degrees.
///
- public bool Expand { get; set; } = true;
-
- ///
- protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- int height = this.CanvasRectangle.Height;
- int width = this.CanvasRectangle.Width;
- Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
- Rectangle sourceBounds = source.Bounds();
-
- using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height))
- {
- Parallel.For(
- 0,
- height,
- configuration.ParallelOptions,
- y =>
- {
- Span targetRow = targetPixels.GetRowSpan(y);
-
- for (int x = 0; x < width; x++)
- {
- var transformedPoint = Point.Skew(new Point(x, y), matrix);
-
- if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
- {
- targetRow[x] = source[transformedPoint.X, transformedPoint.Y];
- }
- }
- });
-
- source.SwapPixelsBuffers(targetPixels);
- }
- }
-
- ///
- protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
- {
- this.processMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0));
- if (this.Expand)
- {
- this.CreateNewCanvas(sourceRectangle, this.processMatrix);
- }
- }
+ public float DegreesY { get; }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs
new file mode 100644
index 0000000000..7403a400e7
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// The base class for all transform processors. Any processor that changes the dimensions of the image should inherit from this.
+ ///
+ /// The pixel format.
+ internal abstract class TransformProcessorBase : CloningImageProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle)
+ => TransformHelpers.UpdateDimensionalMetData(destination);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs
new file mode 100644
index 0000000000..0e91087f90
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Holds the values in an optimized contigous memory region.
+ ///
+ internal class WeightsBuffer : IDisposable
+ {
+ private readonly Buffer2D dataBuffer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The size of the source window
+ /// The size of the destination window
+ public WeightsBuffer(int sourceSize, int destinationSize)
+ {
+ this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize);
+ this.Weights = new WeightsWindow[destinationSize];
+ }
+
+ ///
+ /// Gets the calculated values.
+ ///
+ public WeightsWindow[] Weights { get; }
+
+ ///
+ /// Disposes instance releasing it's backing buffer.
+ ///
+ public void Dispose()
+ {
+ this.dataBuffer.Dispose();
+ }
+
+ ///
+ /// Slices a weights value at the given positions.
+ ///
+ /// The index in destination buffer
+ /// The local left index value
+ /// The local right index value
+ /// The weights
+ public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx)
+ {
+ return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
new file mode 100644
index 0000000000..b0a530514e
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
@@ -0,0 +1,150 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Processing.Processors
+{
+ ///
+ /// Points to a collection of of weights allocated in .
+ ///
+ internal struct WeightsWindow
+ {
+ ///
+ /// The local left index position
+ ///
+ public int Left;
+
+ ///
+ /// The length of the weights window
+ ///
+ public int Length;
+
+ ///
+ /// The index in the destination buffer
+ ///
+ private readonly int flatStartIndex;
+
+ ///
+ /// The buffer containing the weights values.
+ ///
+ private readonly Buffer buffer;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The destination index in the buffer
+ /// The local left index
+ /// The span
+ /// The length of the window
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal WeightsWindow(int index, int left, Buffer2D buffer, int length)
+ {
+ this.flatStartIndex = (index * buffer.Width) + left;
+ this.Left = left;
+ this.buffer = buffer;
+ this.Length = length;
+ }
+
+ ///
+ /// Gets a reference to the first item of the window.
+ ///
+ /// The reference to the first item of the window
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref float GetStartReference()
+ {
+ return ref this.buffer[this.flatStartIndex];
+ }
+
+ ///
+ /// Gets the span representing the portion of the that this window covers
+ ///
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length);
+
+ ///
+ /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance.
+ ///
+ /// The input span of vectors
+ /// The source row position.
+ /// The weighted sum
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX)
+ {
+ ref float horizontalValues = ref this.GetStartReference();
+ int left = this.Left;
+ ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX);
+
+ // Destination color components
+ Vector4 result = Vector4.Zero;
+
+ for (int i = 0; i < this.Length; i++)
+ {
+ float weight = Unsafe.Add(ref horizontalValues, i);
+ Vector4 v = Unsafe.Add(ref vecPtr, i);
+ result += v.Premultiply() * weight;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance.
+ /// Applies to all input vectors.
+ ///
+ /// The input span of vectors
+ /// The source row position.
+ /// The weighted sum
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX)
+ {
+ ref float horizontalValues = ref this.GetStartReference();
+ int left = this.Left;
+ ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX);
+
+ // Destination color components
+ Vector4 result = Vector4.Zero;
+
+ for (int i = 0; i < this.Length; i++)
+ {
+ float weight = Unsafe.Add(ref horizontalValues, i);
+ Vector4 v = Unsafe.Add(ref vecPtr, i);
+ result += v.Premultiply().Expand() * weight;
+ }
+
+ return result.UnPremultiply();
+ }
+
+ ///
+ /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x',
+ /// weighted by weight values, pointed by this instance.
+ ///
+ /// The buffer of input vectors in row first order
+ /// The row position
+ /// The source column position.
+ /// The weighted sum
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY)
+ {
+ ref float verticalValues = ref this.GetStartReference();
+ int left = this.Left;
+
+ // Destination color components
+ Vector4 result = Vector4.Zero;
+
+ for (int i = 0; i < this.Length; i++)
+ {
+ float yw = Unsafe.Add(ref verticalValues, i);
+ int index = left + i + sourceY;
+ result += firstPassPixels[x, index] * yw;
+ }
+
+ return result.UnPremultiply();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs
index 8f2d3db0a9..d20eaefb11 100644
--- a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs
+++ b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing
///
/// Gets or sets the sampler to perform the resize operation.
///
- public IResampler Sampler { get; set; } = new BicubicResampler();
+ public IResampler Sampler { get; set; } = KnownResamplers.Bicubic;
///
/// Gets or sets a value indicating whether to compress
diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs b/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs
new file mode 100644
index 0000000000..2da98497b5
--- /dev/null
+++ b/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs
@@ -0,0 +1,96 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Processing
+{
+ ///
+ /// Contains reusable static instances of known resampling algorithms
+ ///
+ public static class KnownResamplers
+ {
+ ///
+ /// Gets the Bicubic sampler that implements the bicubic kernel algorithm W(x)
+ ///
+ public static IResampler Bicubic { get; } = new BicubicResampler();
+
+ ///
+ /// Gets the Box sampler that implements the box algorithm. Similar to nearest neighbor when upscaling.
+ /// When downscaling the pixels will average, merging pixels together.
+ ///
+ public static IResampler Box { get; } = new BoxResampler();
+
+ ///
+ /// Gets the Catmull-Rom sampler, a well known standard Cubic Filter often used as a interpolation function
+ ///
+ public static IResampler CatmullRom { get; } = new CatmullRomResampler();
+
+ ///
+ /// Gets the Hermite sampler. A type of smoothed triangular interpolation filter that rounds off strong edges while
+ /// preserving flat 'color levels' in the original image.
+ ///
+ public static IResampler Hermite { get; } = new HermiteResampler();
+
+ ///
+ /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 2 pixels.
+ /// This algorithm provides sharpened results when compared to others when downsampling.
+ ///
+ public static IResampler Lanczos2 { get; } = new Lanczos2Resampler();
+
+ ///
+ /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 3 pixels
+ /// This algorithm provides sharpened results when compared to others when downsampling.
+ ///
+ public static IResampler Lanczos3 { get; } = new Lanczos3Resampler();
+
+ ///
+ /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 5 pixels
+ /// This algorithm provides sharpened results when compared to others when downsampling.
+ ///
+ public static IResampler Lanczos5 { get; } = new Lanczos5Resampler();
+
+ ///
+ /// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 8 pixels
+ /// This algorithm provides sharpened results when compared to others when downsampling.
+ ///
+ public static IResampler Lanczos8 { get; } = new Lanczos8Resampler();
+
+ ///
+ /// Gets the Mitchell-Netravali sampler. This seperable cubic algorithm yields a very good equilibrium between
+ /// detail preservation (sharpness) and smoothness.
+ ///
+ public static IResampler MitchellNetravali { get; } = new MitchellNetravaliResampler();
+
+ ///
+ /// Gets the Nearest-Neighbour sampler that implements the nearest neighbor algorithm. This uses a very fast, unscaled filter
+ /// which will select the closest pixel to the new pixels position.
+ ///
+ public static IResampler NearestNeighbor { get; } = new NearestNeighborResampler();
+
+ ///
+ /// Gets the Robidoux sampler. This algorithm developed by Nicolas Robidoux providing a very good equilibrium between
+ /// detail preservation (sharpness) and smoothness comprable to .
+ ///
+ public static IResampler Robidoux { get; } = new RobidouxResampler();
+
+ ///
+ /// Gets the Robidoux Sharp sampler. A sharpend form of the sampler
+ ///
+ public static IResampler RobidouxSharp { get; } = new RobidouxSharpResampler();
+
+ ///
+ /// Gets the Spline sampler. A seperable cubic algorithm similar to but yielding smoother results.
+ ///
+ public static IResampler Spline { get; } = new SplineResampler();
+
+ ///
+ /// Gets the Triangle sampler, otherwise known as Bilinear. This interpolation algorithm can be used where perfect image transformation
+ /// with pixel matching is impossible, so that one can calculate and assign appropriate intensity values to pixels
+ ///
+ public static IResampler Triangle { get; } = new TriangleResampler();
+
+ ///
+ /// Gets the Welch sampler. A high speed algorthm that delivers very sharpened results.
+ ///
+ public static IResampler Welch { get; } = new WelchResampler();
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs
index acb74a8ec4..9e18a24710 100644
--- a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs
+++ b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs
@@ -28,4 +28,4 @@ namespace SixLabors.ImageSharp.Processing
return 0F;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs
index c54267a9f5..a3a62fa49f 100644
--- a/src/ImageSharp/Processing/Transforms/Resize.cs
+++ b/src/ImageSharp/Processing/Transforms/Resize.cs
@@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp
public static IImageProcessingContext Resize(this IImageProcessingContext