Browse Source

Refactor filters to match CSS & SVG methods

af/merge-core
James Jackson-South 8 years ago
parent
commit
c3fda8ff8a
  1. 16
      src/ImageSharp/Processing/ColorMatrix/Brightness.cs
  2. 19
      src/ImageSharp/Processing/ColorMatrix/Contrast.cs
  3. 72
      src/ImageSharp/Processing/ColorMatrix/Grayscale.cs
  4. 6
      src/ImageSharp/Processing/ColorMatrix/Hue.cs
  5. 257
      src/ImageSharp/Processing/ColorMatrix/Matrix4x4Extensions.cs
  6. 14
      src/ImageSharp/Processing/ColorMatrix/Opacity.cs
  7. 22
      src/ImageSharp/Processing/ColorMatrix/Saturate.cs
  8. 31
      src/ImageSharp/Processing/ColorMatrix/Sepia.cs
  9. 4
      src/ImageSharp/Processing/Effects/Invert.cs
  10. 2
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
  11. 2
      src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs
  12. 2
      src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs
  13. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/BrightnessProcessor.cs
  14. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/ContrastProcessor.cs
  15. 62
      src/ImageSharp/Processing/Processors/ColorMatrix/FilterProcessor.cs
  16. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs
  17. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs
  18. 68
      src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs
  19. 30
      src/ImageSharp/Processing/Processors/ColorMatrix/InvertProcessor.cs
  20. 30
      src/ImageSharp/Processing/Processors/ColorMatrix/OpacityProcessor.cs
  21. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/SaturateProcessor.cs
  22. 66
      src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs
  23. 35
      src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs
  24. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs
  25. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  26. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs
  27. 81
      src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs
  28. 87
      src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs
  29. 89
      src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs
  30. 66
      src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs
  31. 2
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  32. 12
      tests/ImageSharp.Tests/Processing/ColorMatrix/BrightnessTest.cs
  33. 14
      tests/ImageSharp.Tests/Processing/ColorMatrix/ContrastTest.cs
  34. 4
      tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs
  35. 0
      tests/ImageSharp.Tests/Processing/ColorMatrix/InvertTest.cs
  36. 29
      tests/ImageSharp.Tests/Processing/ColorMatrix/OpacityTest.cs
  37. 12
      tests/ImageSharp.Tests/Processing/ColorMatrix/SaturateTest.cs
  38. 31
      tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs
  39. 14
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BrightnessTest.cs
  40. 14
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ContrastTest.cs
  41. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/InvertTest.cs
  42. 8
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/OpacityTest.cs
  43. 20
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturateTest.cs
  44. 6
      tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs

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

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

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

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

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

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

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

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

257
src/ImageSharp/Processing/ColorMatrix/Matrix4x4Extensions.cs

@ -0,0 +1,257 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Provides extensions methods for the <see cref="Matrix4x4"/> struct
/// </summary>
// ReSharper disable once InconsistentNaming
public static class Matrix4x4Extensions
{
/// <summary>
/// Create a brightness filter matrix using the given amount.
/// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results.
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateBrightnessFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = amount,
M22 = amount,
M33 = amount,
M44 = 1
};
}
/// <summary>
/// Create a contrast filter matrix using the given amount.
/// </summary>
/// <remarks>
/// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast.
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateContrastFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
float contrast = (-.5F * amount) + .5F;
return new Matrix4x4
{
M11 = amount,
M22 = amount,
M33 = amount,
M41 = contrast,
M42 = contrast,
M43 = contrast,
M44 = 1
};
}
/// <summary>
/// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.601.
/// <see href="https://en.wikipedia.org/wiki/Luma_%28video%29#Rec._601_luma_versus_Rec._709_luma_coefficients"/>
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateGrayscaleBt601Filter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .299F + (.701F * amount),
M12 = .299F - (.299F * amount),
M13 = .299F - (.299F * amount),
M21 = .587F - (.587F * amount),
M22 = .587F + (.413F * amount),
M23 = .587F - (.587F * amount),
M31 = .114F - (.114F * amount),
M32 = .114F - (.114F * amount),
M33 = .114F + (.886F * amount),
M44 = 1
};
}
/// <summary>
/// Create a greyscale filter matrix using the given amount using the formula as specified by ITU-R Recommendation BT.709.
/// <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateGrayscaleBt709Filter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .2126F + (.7874F * amount),
M12 = .2126F - (.2126F * amount),
M13 = .2126F - (.2126F * amount),
M21 = .7152F - (.7152F * amount),
M22 = .7152F + (.2848F * amount),
M23 = .7152F - (.7152F * amount),
M31 = .0722F - (.0722F * amount),
M32 = .0722F - (.0722F * amount),
M33 = .0722F + (.9278F * amount),
M44 = 1
};
}
/// <summary>
/// Create a hue filter matrix using the given angle in degrees.
/// </summary>
/// <param name="degrees">The angle of rotation in degrees.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateHueFilter(float degrees)
{
// Wrap the angle round at 360.
degrees = degrees % 360;
// Make sure it's not negative.
while (degrees < 0)
{
degrees += 360;
}
float radian = MathFExtensions.DegreeToRadian(degrees);
float cosRadian = MathF.Cos(radian);
float sinRadian = MathF.Sin(radian);
// The matrix is set up to preserve the luminance of the image.
// See http://graficaobscura.com/matrix/index.html
// Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
return new Matrix4x4
{
M11 = .213F + (cosRadian * .787F) - (sinRadian * .213F),
M12 = .213F - (cosRadian * .213F) - (sinRadian * 0.143F),
M13 = .213F - (cosRadian * .213F) - (sinRadian * .787F),
M21 = .715F - (cosRadian * .715F) - (sinRadian * .715F),
M22 = .715F + (cosRadian * .285F) + (sinRadian * 0.140F),
M23 = .715F - (cosRadian * .715F) + (sinRadian * .715F),
M31 = .072F - (cosRadian * .072F) + (sinRadian * .928F),
M32 = .072F - (cosRadian * .072F) - (sinRadian * 0.283F),
M33 = .072F + (cosRadian * .928F) + (sinRadian * .072F),
M44 = 1
};
}
/// <summary>
/// Create an invert filter matrix using the given amount.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateInvertFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
float invert = 1F - (2F * amount);
return new Matrix4x4
{
M11 = invert,
M22 = invert,
M33 = invert,
M41 = amount,
M42 = amount,
M43 = amount,
M44 = 1
};
}
/// <summary>
/// Create an opacity filter matrix using the given amount.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateOpacityFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = 1,
M22 = 1,
M33 = 1,
M44 = amount
};
}
/// <summary>
/// Create a saturation filter matrix using the given amount.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateSaturateFilter(float amount)
{
Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount));
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .213F + (.787F * amount),
M12 = .213F - (.213F * amount),
M13 = .213F - (.213F * amount),
M21 = .715F - (.715F * amount),
M22 = .715F + (.285F * amount),
M23 = .715F - (.715F * amount),
M31 = 1F - ((.213F + (.787F * amount)) + (.715F - (.715F * amount))),
M32 = 1F - ((.213F - (.213F * amount)) + (.715F + (.285F * amount))),
M33 = 1F - ((.213F - (.213F * amount)) + (.715F - (.715F * amount))),
M44 = 1
};
}
/// <summary>
/// Create a sepia filter matrix using the given amount.
/// The formula used matches the svg specification. <see href="http://www.w3.org/TR/filter-effects/#sepiaEquivalent"/>
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
/// <returns>The <see cref="Matrix4x4"/></returns>
public static Matrix4x4 CreateSepiaFilter(float amount)
{
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
amount = 1F - amount;
// See https://cs.chromium.org/chromium/src/cc/paint/render_surface_filters.cc
return new Matrix4x4
{
M11 = .393F + (.607F * amount),
M12 = .349F - (.349F * amount),
M13 = .272F - (.272F * amount),
M21 = .769F - (.769F * amount),
M22 = .686F + (.314F * amount),
M23 = .534F - (.534F * amount),
M31 = .189F - (.189F * amount),
M32 = .168F - (.168F * amount),
M33 = .131F + (.869F * amount),
M44 = 1
};
}
}
}

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

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

22
src/ImageSharp/Processing/ColorMatrix/Saturation.cs → src/ImageSharp/Processing/ColorMatrix/Saturate.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;
@ -17,31 +15,39 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Saturation<TPixel>(this IImageProcessingContext<TPixel> source, int amount)
public static IImageProcessingContext<TPixel> Saturate<TPixel>(this IImageProcessingContext<TPixel> source, float amount)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new SaturationProcessor<TPixel>(amount));
source.ApplyProcessor(new SaturateProcessor<TPixel>(amount));
return source;
}
/// <summary>
/// Alters the saturation component of the image.
/// </summary>
/// <remarks>
/// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged.
/// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Saturation<TPixel>(this IImageProcessingContext<TPixel> source, int amount, Rectangle rectangle)
public static IImageProcessingContext<TPixel> Saturate<TPixel>(this IImageProcessingContext<TPixel> source, float amount, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new SaturationProcessor<TPixel>(amount), rectangle);
source.ApplyProcessor(new SaturateProcessor<TPixel>(amount), rectangle);
return source;
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,32 +1,30 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709
/// <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>.
/// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GrayscaleBt709Processor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
internal class GrayscaleBt709Processor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
/// <summary>
/// Initializes a new instance of the <see cref="GrayscaleBt709Processor{TPixel}"/> class.
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public GrayscaleBt709Processor(float amount)
: base(Matrix4x4Extensions.CreateGrayscaleBt709Filter(amount))
{
M11 = .2126F,
M12 = .2126F,
M13 = .2126F,
M21 = .7152F,
M22 = .7152F,
M23 = .7152F,
M31 = .0722F,
M32 = .0722F,
M33 = .0722F,
M44 = 1
};
this.Amount = amount;
}
/// <summary>
/// Gets the proportion of the conversion
/// </summary>
public float Amount { get; }
}
}

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

@ -1,77 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// An <see cref="ImageProcessor{TPixel}"/> to change the hue of an <see cref="Image{TPixel}"/>.
/// Applies a hue filter matrix using the given angle of rotation in degrees
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class HueProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
internal class HueProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="HueProcessor{TPixel}"/> class.
/// </summary>
/// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param>
public HueProcessor(float angle)
/// <param name="degrees">The angle of rotation in degrees</param>
public HueProcessor(float degrees)
: base(Matrix4x4Extensions.CreateHueFilter(degrees))
{
// 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;
this.Degrees = degrees;
}
/// <summary>
/// Gets the rotation value.
/// Gets the angle of rotation in degrees
/// </summary>
public float Angle { get; }
/// <inheritdoc/>
public override Matrix4x4 Matrix { get; }
/// <inheritdoc/>
public override bool Compand => false;
public float Degrees { get; }
}
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -47,6 +47,8 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image<Rgba32> image = file.CreateImage())
{
image.Mutate(x => x.Saturate(1.5F, new Primitives.Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)));
image.Save($"{path}/{file.FileName}");
}
}

12
tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs → tests/ImageSharp.Tests/Processing/ColorMatrix/BrightnessTest.cs

@ -13,19 +13,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
[Fact]
public void Brightness_amount_BrightnessProcessorDefaultsSet()
{
this.operations.Brightness(23);
var processor = this.Verify<BrightnessProcessor<Rgba32>>();
this.operations.Brightness(1.5F);
BrightnessProcessor<Rgba32> processor = this.Verify<BrightnessProcessor<Rgba32>>();
Assert.Equal(23, processor.Value);
Assert.Equal(1.5F, processor.Amount);
}
[Fact]
public void Brightness_amount_rect_BrightnessProcessorDefaultsSet()
{
this.operations.Brightness(23, this.rect);
var processor = this.Verify<BrightnessProcessor<Rgba32>>(this.rect);
this.operations.Brightness(1.5F, this.rect);
BrightnessProcessor<Rgba32> processor = this.Verify<BrightnessProcessor<Rgba32>>(this.rect);
Assert.Equal(23, processor.Value);
Assert.Equal(1.5F, processor.Amount);
}
}
}

14
tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs → tests/ImageSharp.Tests/Processing/ColorMatrix/ContrastTest.cs

@ -1,9 +1,7 @@
// 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;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Effects
@ -13,19 +11,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
[Fact]
public void Contrast_amount_ContrastProcessorDefaultsSet()
{
this.operations.Contrast(23);
var processor = this.Verify<ContrastProcessor<Rgba32>>();
this.operations.Contrast(1.5F);
ContrastProcessor<Rgba32> processor = this.Verify<ContrastProcessor<Rgba32>>();
Assert.Equal(23, processor.Value);
Assert.Equal(1.5F, processor.Amount);
}
[Fact]
public void Contrast_amount_rect_ContrastProcessorDefaultsSet()
{
this.operations.Contrast(23, this.rect);
var processor = this.Verify<ContrastProcessor<Rgba32>>(this.rect);
this.operations.Contrast(1.5F, this.rect);
ContrastProcessor<Rgba32> processor = this.Verify<ContrastProcessor<Rgba32>>(this.rect);
Assert.Equal(23, processor.Value);
Assert.Equal(1.5F, processor.Amount);
}
}
}

4
tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
this.operations.Hue(34f);
var processor = this.Verify<HueProcessor<Rgba32>>();
Assert.Equal(34f, processor.Angle);
Assert.Equal(34f, processor.Degrees);
}
[Fact]
@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
this.operations.Hue(5f, this.rect);
var processor = this.Verify<HueProcessor<Rgba32>>(this.rect);
Assert.Equal(5f, processor.Angle);
Assert.Equal(5f, processor.Degrees);
}
}
}

0
tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs → tests/ImageSharp.Tests/Processing/ColorMatrix/InvertTest.cs

29
tests/ImageSharp.Tests/Processing/ColorMatrix/OpacityTest.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Effects
{
public class OpacityTest : BaseImageOperationsExtensionTest
{
[Fact]
public void Alpha_amount_AlphaProcessorDefaultsSet()
{
this.operations.Opacity(0.2f);
OpacityProcessor<Rgba32> processor = this.Verify<OpacityProcessor<Rgba32>>();
Assert.Equal(.2f, processor.Amount);
}
[Fact]
public void Alpha_amount_rect_AlphaProcessorDefaultsSet()
{
this.operations.Opacity(0.6f, this.rect);
OpacityProcessor<Rgba32> processor = this.Verify<OpacityProcessor<Rgba32>>(this.rect);
Assert.Equal(.6f, processor.Amount);
}
}
}

12
tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs → tests/ImageSharp.Tests/Processing/ColorMatrix/SaturateTest.cs

@ -1,21 +1,19 @@
// 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;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
{
public class SaturationTest : BaseImageOperationsExtensionTest
public class SaturateTest : BaseImageOperationsExtensionTest
{
[Fact]
public void Saturation_amount_SaturationProcessorDefaultsSet()
{
this.operations.Saturation(34);
var processor = this.Verify<SaturationProcessor<Rgba32>>();
this.operations.Saturate(34);
SaturateProcessor<Rgba32> processor = this.Verify<SaturateProcessor<Rgba32>>();
Assert.Equal(34, processor.Amount);
}
@ -23,8 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
[Fact]
public void Saturation_amount_rect_SaturationProcessorDefaultsSet()
{
this.operations.Saturation(5, this.rect);
var processor = this.Verify<SaturationProcessor<Rgba32>>(this.rect);
this.operations.Saturate(5, this.rect);
SaturateProcessor<Rgba32> processor = this.Verify<SaturateProcessor<Rgba32>>(this.rect);
Assert.Equal(5, processor.Amount);
}

31
tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Effects
{
public class AlphaTest : BaseImageOperationsExtensionTest
{
[Fact]
public void Alpha_amount_AlphaProcessorDefaultsSet()
{
this.operations.Alpha(0.2f);
var processor = this.Verify<AlphaProcessor<Rgba32>>();
Assert.Equal(.2f, processor.Value);
}
[Fact]
public void Alpha_amount_rect_AlphaProcessorDefaultsSet()
{
this.operations.Alpha(0.6f, this.rect);
var processor = this.Verify<AlphaProcessor<Rgba32>>(this.rect);
Assert.Equal(.6f, processor.Value);
}
}
}

14
tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs → tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BrightnessTest.cs

@ -11,16 +11,16 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
{
public class BrightnessTest : FileTestBase
{
public static readonly TheoryData<int> BrightnessValues
= new TheoryData<int>
public static readonly TheoryData<float> BrightnessValues
= new TheoryData<float>
{
50,
-50
.5F,
1.5F
};
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)]
public void ImageShouldApplyBrightnessFilter<TPixel>(TestImageProvider<TPixel> provider, int value)
public void ImageShouldApplyBrightnessFilter<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)]
public void ImageShouldApplyBrightnessFilterInBox<TPixel>(TestImageProvider<TPixel> provider, int value)
public void ImageShouldApplyBrightnessFilterInBox<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (var image = source.Clone())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);

14
tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs → tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ContrastTest.cs

@ -11,16 +11,16 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
{
public class ContrastTest : FileTestBase
{
public static readonly TheoryData<int> ContrastValues
= new TheoryData<int>
public static readonly TheoryData<float> ContrastValues
= new TheoryData<float>
{
50,
-50
.5F,
1.5F
};
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)]
public void ImageShouldApplyContrastFilter<TPixel>(TestImageProvider<TPixel> provider, int value)
public void ImageShouldApplyContrastFilter<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)]
public void ImageShouldApplyContrastFilterInBox<TPixel>(TestImageProvider<TPixel> provider, int value)
public void ImageShouldApplyContrastFilterInBox<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (var image = source.Clone())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);

2
tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs → tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/InvertTest.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (var image = source.Clone())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);

8
tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs → tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/OpacityTest.cs

@ -9,7 +9,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
{
public class AlphaTest : FileTestBase
public class OpacityTest : FileTestBase
{
public static readonly TheoryData<float> AlphaValues
= new TheoryData<float>
@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Alpha(value));
image.Mutate(x => x.Opacity(value));
image.DebugSave(provider, value);
}
}
@ -36,11 +36,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (var image = source.Clone())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);
image.Mutate(x => x.Alpha(value, bounds));
image.Mutate(x => x.Opacity(value, bounds));
image.DebugSave(provider, value);
ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds);

20
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs → tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturateTest.cs

@ -9,38 +9,38 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
{
public class SaturationTest : FileTestBase
public class SaturateTest : FileTestBase
{
public static readonly TheoryData<int> SaturationValues
= new TheoryData<int>
public static readonly TheoryData<float> SaturationValues
= new TheoryData<float>
{
50 ,
-50 ,
.5f,
1.5F,
};
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)]
public void ImageShouldApplySaturationFilter<TPixel>(TestImageProvider<TPixel> provider, int value)
public void ImageShouldApplySaturationFilter<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Saturation(value));
image.Mutate(x => x.Saturate(value));
image.DebugSave(provider, value);
}
}
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)]
public void ImageShouldApplySaturationFilterInBox<TPixel>(TestImageProvider<TPixel> provider, int value)
public void ImageShouldApplySaturationFilterInBox<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (var image = source.Clone())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);
image.Mutate(x => x.Saturation(value, bounds));
image.Mutate(x => x.Saturate(value, bounds));
image.DebugSave(provider, value);
ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds);

6
tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests
{
if (pngColorType != PngColorType.RgbWithAlpha)
{
sourceImage.Mutate(c => c.Alpha(1));
sourceImage.Mutate(c => c.Opacity(1));
}
var encoder = new PngEncoder() { PngColorType = pngColorType };
@ -93,12 +93,12 @@ namespace SixLabors.ImageSharp.Tests
using (Image<TPixel> original = provider.GetImage())
{
original.Mutate(c => c.Alpha(1));
original.Mutate(c => c.Opacity(1));
using (var sdBitmap = new System.Drawing.Bitmap(path))
{
using (Image<TPixel> resaved = SystemDrawingBridge.FromFromRgb24SystemDrawingBitmap<TPixel>(sdBitmap))
{
resaved.Mutate(c => c.Alpha(1));
resaved.Mutate(c => c.Opacity(1));
ImageComparer comparer = ImageComparer.Exact;
comparer.VerifySimilarity(original, resaved);
}

Loading…
Cancel
Save