mirror of https://github.com/SixLabors/ImageSharp
44 changed files with 764 additions and 641 deletions
@ -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 |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,32 +1,30 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors |
namespace SixLabors.ImageSharp.Processing.Processors |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.601
|
/// Applies a greyscale filter matrix using the given amount and 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>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
internal class GrayscaleBt601Processor<TPixel> : ColorMatrixProcessor<TPixel> |
internal class GrayscaleBt601Processor<TPixel> : FilterProcessor<TPixel> |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
/// <inheritdoc/>
|
/// <summary>
|
||||
public override Matrix4x4 Matrix => new Matrix4x4 |
/// Initializes a new instance of the <see cref="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, |
this.Amount = amount; |
||||
M12 = .299F, |
} |
||||
M13 = .299F, |
|
||||
M21 = .587F, |
/// <summary>
|
||||
M22 = .587F, |
/// Gets the proportion of the conversion
|
||||
M23 = .587F, |
/// </summary>
|
||||
M31 = .114F, |
public float Amount { get; } |
||||
M32 = .114F, |
|
||||
M33 = .114F, |
|
||||
M44 = 1 |
|
||||
}; |
|
||||
} |
} |
||||
} |
} |
||||
@ -1,32 +1,30 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors |
namespace SixLabors.ImageSharp.Processing.Processors |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709
|
/// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709
|
||||
/// <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>.
|
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
internal class GrayscaleBt709Processor<TPixel> : ColorMatrixProcessor<TPixel> |
internal class GrayscaleBt709Processor<TPixel> : FilterProcessor<TPixel> |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
/// <inheritdoc/>
|
/// <summary>
|
||||
public override Matrix4x4 Matrix => new Matrix4x4 |
/// Initializes a new instance of the <see cref="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, |
this.Amount = amount; |
||||
M12 = .2126F, |
} |
||||
M13 = .2126F, |
|
||||
M21 = .7152F, |
/// <summary>
|
||||
M22 = .7152F, |
/// Gets the proportion of the conversion
|
||||
M23 = .7152F, |
/// </summary>
|
||||
M31 = .0722F, |
public float Amount { get; } |
||||
M32 = .0722F, |
|
||||
M33 = .0722F, |
|
||||
M44 = 1 |
|
||||
}; |
|
||||
} |
} |
||||
} |
} |
||||
@ -1,77 +1,29 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors |
namespace SixLabors.ImageSharp.Processing.Processors |
||||
{ |
{ |
||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
internal class HueProcessor<TPixel> : FilterProcessor<TPixel> |
||||
internal class HueProcessor<TPixel> : ColorMatrixProcessor<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HueProcessor{TPixel}"/> class.
|
/// Initializes a new instance of the <see cref="HueProcessor{TPixel}"/> class.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param>
|
/// <param name="degrees">The angle of rotation in degrees</param>
|
||||
public HueProcessor(float angle) |
public HueProcessor(float degrees) |
||||
|
: base(Matrix4x4Extensions.CreateHueFilter(degrees)) |
||||
{ |
{ |
||||
// Wrap the angle round at 360.
|
this.Degrees = degrees; |
||||
angle = angle % 360; |
|
||||
|
|
||||
// Make sure it's not negative.
|
|
||||
while (angle < 0) |
|
||||
{ |
|
||||
angle += 360; |
|
||||
} |
|
||||
|
|
||||
this.Angle = angle; |
|
||||
|
|
||||
float radians = MathFExtensions.DegreeToRadian(angle); |
|
||||
float cosradians = MathF.Cos(radians); |
|
||||
float sinradians = MathF.Sin(radians); |
|
||||
|
|
||||
float lumR = .213F; |
|
||||
float lumG = .715F; |
|
||||
float lumB = .072F; |
|
||||
|
|
||||
float oneMinusLumR = 1 - lumR; |
|
||||
float oneMinusLumG = 1 - lumG; |
|
||||
float oneMinusLumB = 1 - lumB; |
|
||||
|
|
||||
// The matrix is set up to preserve the luminance of the image.
|
|
||||
// See http://graficaobscura.com/matrix/index.html
|
|
||||
// Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx
|
|
||||
var matrix4X4 = new Matrix4x4 |
|
||||
{ |
|
||||
M11 = lumR + (cosradians * oneMinusLumR) - (sinradians * lumR), |
|
||||
M12 = lumR - (cosradians * lumR) - (sinradians * 0.143F), |
|
||||
M13 = lumR - (cosradians * lumR) - (sinradians * oneMinusLumR), |
|
||||
M21 = lumG - (cosradians * lumG) - (sinradians * lumG), |
|
||||
M22 = lumG + (cosradians * oneMinusLumG) + (sinradians * 0.140F), |
|
||||
M23 = lumG - (cosradians * lumG) + (sinradians * lumG), |
|
||||
M31 = lumB - (cosradians * lumB) + (sinradians * oneMinusLumB), |
|
||||
M32 = lumB - (cosradians * lumB) - (sinradians * 0.283F), |
|
||||
M33 = lumB + (cosradians * oneMinusLumB) + (sinradians * lumB), |
|
||||
M44 = 1 |
|
||||
}; |
|
||||
|
|
||||
this.Matrix = matrix4X4; |
|
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Gets the rotation value.
|
/// Gets the angle of rotation in degrees
|
||||
/// </summary>
|
/// </summary>
|
||||
public float Angle { get; } |
public float Degrees { get; } |
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override Matrix4x4 Matrix { get; } |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override bool Compand => false; |
|
||||
} |
} |
||||
} |
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; } |
||||
|
} |
||||
|
} |
||||
@ -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; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,35 +1,30 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors |
namespace SixLabors.ImageSharp.Processing.Processors |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Converts the colors of the image to their sepia equivalent.
|
/// Applies 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>
|
/// </summary>
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
internal class SepiaProcessor<TPixel> : ColorMatrixProcessor<TPixel> |
internal class SepiaProcessor<TPixel> : FilterProcessor<TPixel> |
||||
where TPixel : struct, IPixel<TPixel> |
where TPixel : struct, IPixel<TPixel> |
||||
{ |
{ |
||||
/// <inheritdoc/>
|
/// <summary>
|
||||
public override Matrix4x4 Matrix => new Matrix4x4 |
/// Initializes a new instance of the <see cref="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, |
this.Amount = amount; |
||||
M12 = .349F, |
} |
||||
M13 = .272F, |
|
||||
M21 = .769F, |
|
||||
M22 = .686F, |
|
||||
M23 = .534F, |
|
||||
M31 = .189F, |
|
||||
M32 = .168F, |
|
||||
M33 = .131F, |
|
||||
M44 = 1 |
|
||||
}; |
|
||||
|
|
||||
/// <inheritdoc/>
|
/// <summary>
|
||||
public override bool Compand => false; |
/// Gets the proportion of the conversion
|
||||
|
/// </summary>
|
||||
|
public float Amount { get; } |
||||
} |
} |
||||
} |
} |
||||
@ -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); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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()); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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()); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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)); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue