diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 4aebe00fb..133506827 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -155,10 +155,10 @@ namespace ImageSharp.Drawing.Brushes { for (int i = 0; i < scanline.Length; i++) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); int patternX = (x + i) % this.pattern.Width; - overlay[i] = this.pattern[y, x]; + overlay[i] = this.pattern[patternY, patternX]; } BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 52c275595..9ea9b549a 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using Drawing.Processors; + using ImageSharp.Drawing; using ImageSharp.PixelFormats; /// @@ -14,31 +15,17 @@ namespace ImageSharp /// public static partial class ImageExtensions { - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The . - public static Image Blend(this Image source, Image image, int percent = 50) - where TPixel : struct, IPixel - { - return DrawImage(source, image, percent, default(Size), default(Point)); - } - /// /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. /// The image to blend with the currently processing image. /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 100. /// The size to draw the blended image. /// The location to draw the blended image. + /// The options. /// The . - public static Image DrawImage(this Image source, Image image, int percent, Size size, Point location) + public static Image DrawImage(this Image source, Image image, Size size, Point location, GraphicsOptions options) where TPixel : struct, IPixel { if (size == default(Size)) @@ -51,27 +38,56 @@ namespace ImageSharp location = Point.Empty; } - source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds); + source.ApplyProcessor(new DrawImageProcessor(image, size, location, options), source.Bounds); return source; } /// /// Draws the given image together with the current one by blending their pixels. /// + /// The pixel format. /// The image this method extends. /// The image to blend with the currently processing image. - /// Pixel function effect to apply on every pixel + /// The opacity of the image image to blend. Must be between 0 and 1. + /// The . + public static Image Blend(this Image source, Image image, float percent) + where TPixel : struct, IPixel + { + GraphicsOptions options = GraphicsOptions.Default; + options.BlendPercentage = percent; + return DrawImage(source, image, default(Size), default(Point), options); + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The size to draw the blended image. - /// The location to draw the blended image. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The blending mode. + /// The opacity of the image image to blend. Must be between 0 and 1. /// The . - public static Image DrawImage(this Image source, Image image, PixelTransformMode mode, int percent, Size size, Point location) + public static Image Blend(this Image source, Image image, PixelBlenderMode blender, float percent) where TPixel : struct, IPixel { - Func pixelFunc = mode.GetPixelFunction(); + GraphicsOptions options = GraphicsOptions.Default; + options.BlendPercentage = percent; + options.BlenderMode = blender; + return DrawImage(source, image, default(Size), default(Point), options); + } - return DrawImage(source, image, pixelFunc, percent, size, location); + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The options, including the blending type and belnding amount. + /// The . + public static Image Blend(this Image source, Image image, GraphicsOptions options) + where TPixel : struct, IPixel + { + return DrawImage(source, image, default(Size), default(Point), options); } /// @@ -79,27 +95,37 @@ namespace ImageSharp /// /// The image this method extends. /// The image to blend with the currently processing image. - /// Pixel function effect to apply on every pixel /// The pixel format. /// The opacity of the image image to blend. Must be between 0 and 100. /// The size to draw the blended image. /// The location to draw the blended image. /// The . - public static Image DrawImage(this Image source, Image image, Func pixelFunc, int percent, Size size, Point location) + public static Image DrawImage(this Image source, Image image, float percent, Size size, Point location) where TPixel : struct, IPixel { - if (size == default(Size)) - { - size = new Size(image.Width, image.Height); - } - - if (location == default(Point)) - { - location = Point.Empty; - } + GraphicsOptions options = GraphicsOptions.Default; + options.BlendPercentage = percent; + return source.DrawImage(image, size, location, options); + } - source.ApplyProcessor(new DrawImageEffectProcessor(image, size, location, pixelFunc, percent), source.Bounds); - return source; + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The pixel format. + /// The type of bending to apply. + /// The opacity of the image image to blend. Must be between 0 and 1. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) + where TPixel : struct, IPixel + { + GraphicsOptions options = GraphicsOptions.Default; + options.BlenderMode = blender; + options.BlendPercentage = percent; + return source.DrawImage(image, size, location, options); } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/GraphicsOptions.cs b/src/ImageSharp.Drawing/GraphicsOptions.cs index 2ceb654d3..03176addf 100644 --- a/src/ImageSharp.Drawing/GraphicsOptions.cs +++ b/src/ImageSharp.Drawing/GraphicsOptions.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Drawing /// If set to true [enable antialiasing]. public GraphicsOptions(bool enableAntialiasing) { - this.blenderMode = PixelBlenderMode.Default; + this.blenderMode = PixelBlenderMode.Normal; this.blendPercentage = 1; this.antialiasSubpixelDepth = 16; this.antialias = enableAntialiasing; @@ -64,6 +64,10 @@ namespace ImageSharp.Drawing set => this.blendPercentage = value; } + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. + /// /// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index e2a9ef024..95c5b4a2f 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -18,19 +18,22 @@ namespace ImageSharp.Drawing.Processors internal class DrawImageProcessor : ImageProcessor where TPixel : struct, IPixel { + private readonly PixelBlender blender; + /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The size to draw the blended image. /// The location to draw the blended image. - /// The opacity of the image to blend. Between 0 and 100. - public DrawImageProcessor(Image image, Size size, Point location, int alpha = 100) + /// The opacity of the image to blend. Between 0 and 100. + public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options) { - Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); + Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage)); this.Image = image; this.Size = size; - this.Alpha = alpha; + this.Alpha = options.BlendPercentage; + this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); this.Location = location; } @@ -42,7 +45,7 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the alpha percentage value. /// - public int Alpha { get; } + public float Alpha { get; } /// /// Gets the size to draw the blended image. @@ -57,43 +60,60 @@ namespace ImageSharp.Drawing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - if (this.Image.Bounds.Size != this.Size) + Image disposableImage = null; + Image targetImage = this.Image; + + try { - // should Resize be moved to core? - this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); - } + if (targetImage.Bounds.Size != this.Size) + { + // should Resize be moved to core? + targetImage = disposableImage = new Image(this.Image).Resize(this.Size.Width, this.Size.Height); + } - // Align start/end positions. - Rectangle bounds = this.Image.Bounds; - int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); - int minY = Math.Max(this.Location.Y, sourceRectangle.Y); - int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + // Align start/end positions. + Rectangle bounds = this.Image.Bounds; + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - float alpha = this.Alpha / 100F; + int width = maxX - minX; + using (Buffer amount = new Buffer(width)) + using (PixelAccessor toBlendPixels = this.Image.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) + { + for (int i = 0; i < width; i++) + { + amount[i] = this.Alpha; + } - using (PixelAccessor toBlendPixels = this.Image.Lock()) - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => { - Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); - Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4(); + BufferSpan background = sourcePixels.GetRowSpan(y).Slice(minX, width); + BufferSpan foreground = toBlendPixels.GetRowSpan(y - minY).Slice(0, width); + this.blender.Compose(background, background, foreground, amount); - // Lerping colors is dependent on the alpha of the blended color - backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha); - - TPixel packed = default(TPixel); - packed.PackFromVector4(backgroundVector); - sourcePixels[x, y] = packed; - } + // for (int x = minX; x < maxX; x++) + // { + // Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); + // Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4(); + // // Lerping colors is dependent on the alpha of the blended color + // backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha); + // TPixel packed = default(TPixel); + // packed.PackFromVector4(backgroundVector); + // sourcePixels[x, y] = packed; + // } }); + } + } + finally + { + disposableImage?.Dispose(); } } } diff --git a/src/ImageSharp/ImageProcessor.cs b/src/ImageSharp/ImageProcessor.cs index 7b9f74547..745b25fb6 100644 --- a/src/ImageSharp/ImageProcessor.cs +++ b/src/ImageSharp/ImageProcessor.cs @@ -41,7 +41,11 @@ namespace ImageSharp.Processing } catch (Exception ex) { +#if DEBUG + throw; +#else throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index c189fefdc..ebb08757b 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -15,69 +15,48 @@ namespace ImageSharp.PixelFormats public enum PixelBlenderMode { /// - /// The default composition mode. + /// Default blending mode, also known as "Normal" or "Alpha Blending" /// - /// uses PremultipliedLerpTransform - Default = 0, + Normal = 0, /// - /// Normal transform. - /// - Normal, - - /// - /// Multiply Transform. + /// Backdrop + Source /// Multiply, /// - /// Screen Transform. + /// Backdrop + Source /// - Screen, + Add, /// - /// HardLight Transform. + /// Backdrop - Source /// - HardLight, + Substract, /// - /// Overlay Transform. + /// Screen effect /// - Overlay, + Screen, /// - /// Darken Transform. + /// Darken effect /// Darken, /// - /// Lighten Transform. + /// Lighten effect /// Lighten, /// - /// SoftLight Transform. - /// - SoftLight, - - /// - /// Dodge Transform. - /// - Dodge, - - /// - /// Burn Transform. + /// Overlay effect /// - Burn, - - /// - /// Difference Transform. - /// - Difference, + Overlay, /// - /// Exclusion Transform. + /// Hard light effect /// - Exclusion + HardLight } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs similarity index 67% rename from src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs rename to src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs index f7a71e6a1..6a77034f2 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -13,16 +13,18 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Abstract base class for calling pixel composition functions /// /// The type of the pixel - internal class DefaultBurnPixelBlender : PixelBlender + internal class DefaultAddPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultAddPixelBlender Instance { get; } = new DefaultAddPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.Burn(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.AddFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.Burn(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.AddFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs index 521ae7bf1..62da4d934 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs @@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultDarkenPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultDarkenPixelBlender Instance { get; } = new DefaultDarkenPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.Darken(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.DarkenFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.Darken(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.DarkenFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs deleted file mode 100644 index 08e1d36dd..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - using ImageSharp.PixelFormats; - - /// - /// Abstract base class for calling pixel composition functions - /// - /// The type of the pixel - internal class DefaultDifferencePixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - public override TPixel Compose(TPixel background, TPixel source, float amount) - { - Vector4 result = Vector4BlendTransforms.Difference(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; - } - - /// - public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) - { - Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); - - for (int i = 0; i < destination.Length; i++) - { - Vector4 result = Vector4BlendTransforms.Difference(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs deleted file mode 100644 index cd2c4e4b8..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - using ImageSharp.PixelFormats; - - /// - /// Abstract base class for calling pixel composition functions - /// - /// The type of the pixel - internal class DefaultDodgePixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - public override TPixel Compose(TPixel background, TPixel source, float amount) - { - Vector4 result = Vector4BlendTransforms.Dodge(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; - } - - /// - public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) - { - Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); - - for (int i = 0; i < destination.Length; i++) - { - Vector4 result = Vector4BlendTransforms.Dodge(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs deleted file mode 100644 index 817fe82c7..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - using ImageSharp.PixelFormats; - - /// - /// Abstract base class for calling pixel composition functions - /// - /// The type of the pixel - internal class DefaultExclusionPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - public override TPixel Compose(TPixel background, TPixel source, float amount) - { - Vector4 result = Vector4BlendTransforms.Exclusion(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; - } - - /// - public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) - { - Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); - - for (int i = 0; i < destination.Length; i++) - { - Vector4 result = Vector4BlendTransforms.Exclusion(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs index 41c1005f6..46e3023a7 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs @@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultHardLightPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultHardLightPixelBlender Instance { get; } = new DefaultHardLightPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.HardLight(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.HardLightFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.HardLight(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.HardLightFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs index 6de12372b..0ee6f151d 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultLightenPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultLightenPixelBlender Instance { get; } = new DefaultLightenPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.Lighten(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.LightenFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.Lighten(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.LightenFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs index 8240c2b87..f9f98d7ea 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs @@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultMultiplyPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultMultiplyPixelBlender Instance { get; } = new DefaultMultiplyPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.Multiply(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.MultiplyFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.Multiply(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.MultiplyFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs index cc58498b3..e55be7202 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs @@ -6,6 +6,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders { using System; + using System.Numerics; using ImageSharp.PixelFormats; /// @@ -15,10 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultNormalPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultNormalPixelBlender Instance { get; } = new DefaultNormalPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - return source; + return PorterDuffFunctions.NormalBlendFunction(background, source, amount); } /// @@ -30,7 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - destination[i] = source[i]; + destination[i] = PorterDuffFunctions.NormalBlendFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs index 042543608..c6d2cfbcd 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs @@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultOverlayPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultOverlayPixelBlender Instance { get; } = new DefaultOverlayPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.Overlay(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.OverlayFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.Overlay(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.OverlayFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs deleted file mode 100644 index 32ab08796..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - using ImageSharp.PixelFormats; - - /// - /// Abstract base class for calling pixel composition functions - /// - /// The type of the pixel - internal class DefaultPremultipliedLerpPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - public override TPixel Compose(TPixel background, TPixel source, float amount) - { - Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background.ToVector4(), source.ToVector4(), amount); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; - } - - /// - public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) - { - Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); - - for (int i = 0; i < destination.Length; i++) - { - Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background[i].ToVector4(), source[i].ToVector4(), amount[i]); - destination[i].PackFromVector4(result); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs index be578af81..cc8f77495 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs @@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders internal class DefaultScreenPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultScreenPixelBlender Instance { get; } = new DefaultScreenPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.Screen(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.ScreenFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.Screen(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.ScreenFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs similarity index 69% rename from src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs rename to src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs index 357e084c9..0d6428073 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -13,16 +13,18 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Abstract base class for calling pixel composition functions /// /// The type of the pixel - internal class DefaultSoftLightPixelBlender : PixelBlender + internal class DefaultSubstractPixelBlender : PixelBlender where TPixel : struct, IPixel { + /// + /// Gets the static instance of this blender. + /// + public static DefaultSubstractPixelBlender Instance { get; } = new DefaultSubstractPixelBlender(); + /// public override TPixel Compose(TPixel background, TPixel source, float amount) { - Vector4 result = Vector4BlendTransforms.SoftLight(background.ToVector4(), source.ToVector4()); - TPixel resultPixel = default(TPixel); - resultPixel.PackFromVector4(result); - return resultPixel; + return PorterDuffFunctions.SubstractFunction(background, source, amount); } /// @@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders for (int i = 0; i < destination.Length; i++) { - Vector4 result = Vector4BlendTransforms.SoftLight(background[i].ToVector4(), source[i].ToVector4()); - destination[i].PackFromVector4(result); + destination[i] = PorterDuffFunctions.SubstractFunction(destination[i], source[i], amount[i]); } } } diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index 5ab3449f2..e2fb377ba 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -34,56 +34,6 @@ namespace ImageSharp.PixelFormats /// private PixelBlender screenBlender = new DefaultScreenPixelBlender(); - /// - /// Gets the HardLightBlender. - /// - private PixelBlender hardLightBlender = new DefaultHardLightPixelBlender(); - - /// - /// Gets the OverlayBlender. - /// - private PixelBlender overlayBlender = new DefaultOverlayPixelBlender(); - - /// - /// Gets the DarkenBlender. - /// - private PixelBlender darkenBlender = new DefaultDarkenPixelBlender(); - - /// - /// Gets the LightenBlender. - /// - private PixelBlender lightenBlender = new DefaultLightenPixelBlender(); - - /// - /// Gets the SoftLightBlender. - /// - private PixelBlender softLightBlender = new DefaultSoftLightPixelBlender(); - - /// - /// Gets the DodgeBlender. - /// - private PixelBlender dodgeBlender = new DefaultDodgePixelBlender(); - - /// - /// Gets the BurnBlender. - /// - private PixelBlender burnBlender = new DefaultBurnPixelBlender(); - - /// - /// Gets the DifferenceBlender. - /// - private PixelBlender differenceBlender = new DefaultDifferencePixelBlender(); - - /// - /// Gets the DifferenceBlender. - /// - private PixelBlender exclusionBlender = new DefaultExclusionPixelBlender(); - - /// - /// Gets the PremultipliedLerpBlender. - /// - private PixelBlender premultipliedLerpBlender = new DefaultPremultipliedLerpPixelBlender(); - /// /// Find an instance of the pixel blender. /// @@ -93,32 +43,25 @@ namespace ImageSharp.PixelFormats { switch (mode) { - case PixelBlenderMode.Normal: - return this.normalBlender; case PixelBlenderMode.Multiply: - return this.multiplyBlender; + return DefaultMultiplyPixelBlender.Instance; + case PixelBlenderMode.Add: + return DefaultAddPixelBlender.Instance; + case PixelBlenderMode.Substract: + return DefaultSubstractPixelBlender.Instance; case PixelBlenderMode.Screen: - return this.screenBlender; - case PixelBlenderMode.HardLight: - return this.hardLightBlender; - case PixelBlenderMode.Overlay: - return this.overlayBlender; + return DefaultScreenPixelBlender.Instance; case PixelBlenderMode.Darken: - return this.darkenBlender; + return DefaultDarkenPixelBlender.Instance; case PixelBlenderMode.Lighten: - return this.lightenBlender; - case PixelBlenderMode.SoftLight: - return this.softLightBlender; - case PixelBlenderMode.Dodge: - return this.dodgeBlender; - case PixelBlenderMode.Burn: - return this.burnBlender; - case PixelBlenderMode.Difference: - return this.differenceBlender; - case PixelBlenderMode.Exclusion: - return this.exclusionBlender; + return DefaultLightenPixelBlender.Instance; + case PixelBlenderMode.Overlay: + return DefaultOverlayPixelBlender.Instance; + case PixelBlenderMode.HardLight: + return DefaultHardLightPixelBlender.Instance; + case PixelBlenderMode.Normal: default: - return this.premultipliedLerpBlender; + return DefaultNormalPixelBlender.Instance; } } } diff --git a/src/ImageSharp/PixelFormats/PixelTransformMode.cs b/src/ImageSharp/PixelFormats/PixelTransformMode.cs deleted file mode 100644 index 159b27515..000000000 --- a/src/ImageSharp/PixelFormats/PixelTransformMode.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats -{ - /// - /// Porter Duff Blending composition modes - /// - public enum PixelTransformMode - { - /// - /// Default blending mode, also known as "Normal" or "Alpha Blending" - /// - Normal, - - /// - /// Backdrop + Source - /// - Multiply, - - /// - /// Backdrop + Source - /// - Add, - - /// - /// Backdrop - Source - /// - Substract, - - /// - /// Screen effect - /// - Screen, - - /// - /// Darken effect - /// - Darken, - - /// - /// Lighten effect - /// - Lighten, - - /// - /// Overlay effect - /// - Overlay, - - /// - /// Hard light effect - /// - HardLight - } -} diff --git a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs deleted file mode 100644 index 4ecddfded..000000000 --- a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Text; - using PixelFormats; - - /// - /// Extensions to retrieve the appropiate pixel transformation functions for - /// - public static class PixelTransformModeExtensions - { - /// - /// Gets a pixel transformation function - /// - /// The pixel format used for both Backdrop and Source - /// The Duff Porter mode - /// A function that transforms a Backdrop and Source colors into a final color - public static Func GetPixelFunction(this PixelTransformMode mode) - where TPixel : IPixel - { - return mode.GetPixelFunction(); - } - - /// - /// Gets a pixel transformation function - /// - /// The pixel format used for Backdrop and Output - /// The pixel format used for Source - /// The Duff Porter mode - /// A function that transforms a Backdrop and Source colors into a final color - public static Func GetPixelFunction(this PixelTransformMode mode) - where TBckPixel : IPixel - where TSrcPixel : IPixel - { - switch (mode) - { - case PixelTransformMode.Normal: return PorterDuffFunctions.NormalBlendFunction; - case PixelTransformMode.Multiply: return PorterDuffFunctions.MultiplyFunction; - case PixelTransformMode.Add: return PorterDuffFunctions.AddFunction; - case PixelTransformMode.Substract: return PorterDuffFunctions.SubstractFunction; - case PixelTransformMode.Screen: return PorterDuffFunctions.ScreenFunction; - case PixelTransformMode.Darken: return PorterDuffFunctions.DarkenFunction; - case PixelTransformMode.Lighten: return PorterDuffFunctions.LightenFunction; - case PixelTransformMode.Overlay: return PorterDuffFunctions.OverlayFunction; - case PixelTransformMode.HardLight: return PorterDuffFunctions.HardLightFunction; - - default: throw new NotImplementedException(nameof(mode)); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs index 1c9f6b842..bbf1811af 100644 --- a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs @@ -11,8 +11,7 @@ namespace ImageSharp.PixelFormats /// /// Collection of Porter Duff alpha blending functions /// - /// Backdrop Pixel Format - /// Source Pixel Format + /// Pixel Format /// /// These functions are designed to be a general solution for all color cases, /// that is, they take in account the alpha value of both the backdrop @@ -21,9 +20,8 @@ namespace ImageSharp.PixelFormats /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - internal static class PorterDuffFunctions - where TBckPixel : IPixel - where TsrcPixel : IPixel + internal static class PorterDuffFunctions + where TPixel : IPixel { /// /// Source over backdrop @@ -32,7 +30,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel NormalBlendFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -53,7 +52,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel MultiplyFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -74,7 +74,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel AddFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -95,7 +96,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel SubstractFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -116,7 +118,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel ScreenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -137,7 +140,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel DarkenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -158,7 +162,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel LightenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -179,7 +184,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel OverlayFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -204,7 +210,8 @@ namespace ImageSharp.PixelFormats /// Source color /// Opacity applied to Source Alpha /// Output color - public static TBckPixel HardLightFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity) { Vector4 l = source.ToVector4(); l.W *= opacity; @@ -242,7 +249,7 @@ namespace ImageSharp.PixelFormats /// Desired transformed color, without taking Alpha channel in account /// The final color [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) + private static TPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) { DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); @@ -258,7 +265,7 @@ namespace ImageSharp.PixelFormats xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; xform.W = a; - TBckPixel packed = default(TBckPixel); + TPixel packed = default(TPixel); packed.PackFromVector4(xform); return packed; diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index fba3fbb8e..ad7846d84 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -76,18 +76,19 @@ namespace ImageSharp.Processing.Processors } // TODO move GraphicOptions into core so all processes can use it. - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelBlenderMode.Default); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelBlenderMode.Normal); for (int y = minY; y < maxY; y++) { int offsetY = y - startY; int offsetX = minX - startX; for (int i = 0; i < width; i++) { - float distance = Vector2.Distance(centre, new Vector2((i + offsetX), offsetY)); - amounts[i] = 1 - (.95F * (distance / maxDistance)); + float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); + amounts[i] = (1 - (.95F * (distance / maxDistance))).Clamp(0, 1); } BufferSpan destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); + blender.Compose(destination, destination, rowColors, amounts); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs index 6c2b8cfc6..8f0247bc2 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs @@ -74,8 +74,6 @@ namespace ImageSharp.Processing.Processors rowColors[i] = glowColor; } - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelBlenderMode.Default); - Parallel.For( minY, maxY, diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs index 78f0a0870..f71de1466 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.Drawing; using ImageSharp.PixelFormats; using Xunit; @@ -16,7 +17,7 @@ namespace ImageSharp.Tests { string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect"); - PixelTransformMode[] modes = (PixelTransformMode[])System.Enum.GetValues(typeof(PixelTransformMode)); + PixelBlenderMode[] modes = (PixelBlenderMode[])System.Enum.GetValues(typeof(PixelBlenderMode)); using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage()) { @@ -24,14 +25,17 @@ namespace ImageSharp.Tests { using (Image image = file.CreateImage()) { - foreach (PixelTransformMode mode in modes) + foreach (PixelBlenderMode mode in modes) { using (FileStream output = File.OpenWrite($"{path}/{mode}.{file.FileName}")) { Size size = new Size(image.Width / 2, image.Height / 2); Point loc = new Point(image.Width / 4, image.Height / 4); - image.DrawImage(blend, mode, 75, size, loc).Save(output); + image.DrawImage(blend, size, loc, new GraphicsOptions() { + BlenderMode = mode, + BlendPercentage = .75f + }).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 3a59de624..f87a6170d 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Tests { using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) + image.DrawImage(blend, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) .Save(output); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs index 6b2bd7c6b..0618114a0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs @@ -14,31 +14,25 @@ namespace ImageSharp.Tests.PixelFormats { public static TheoryData blenderMappings = new TheoryData() { - { new TestPixel(), typeof(DefaultPremultipliedLerpPixelBlender), PixelBlenderMode.Default }, { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultSoftLightPixelBlender), PixelBlenderMode.SoftLight }, - { new TestPixel(), typeof(DefaultDodgePixelBlender), PixelBlenderMode.Dodge }, - { new TestPixel(), typeof(DefaultBurnPixelBlender), PixelBlenderMode.Burn }, - { new TestPixel(), typeof(DefaultDifferencePixelBlender), PixelBlenderMode.Difference }, - { new TestPixel(), typeof(DefaultExclusionPixelBlender), PixelBlenderMode.Exclusion }, + { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, - { new TestPixel(), typeof(DefaultPremultipliedLerpPixelBlender), PixelBlenderMode.Default }, { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultSoftLightPixelBlender), PixelBlenderMode.SoftLight }, - { new TestPixel(), typeof(DefaultDodgePixelBlender), PixelBlenderMode.Dodge }, - { new TestPixel(), typeof(DefaultBurnPixelBlender), PixelBlenderMode.Burn }, - { new TestPixel(), typeof(DefaultDifferencePixelBlender), PixelBlenderMode.Difference }, - { new TestPixel(), typeof(DefaultExclusionPixelBlender), PixelBlenderMode.Exclusion } + { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, }; [Theory]