From 96d2f6ddcc985719e04dee72bf427900c3a27e52 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Mon, 20 Aug 2018 17:41:58 +0200 Subject: [PATCH 01/10] Split PixelBlendMode enumeration into PixelColorBlendingMode and PixelAlphaCompositionMode --- .../Processing/BrushApplicator.cs | 2 +- .../Processing/DrawImageExtensions.cs | 4 +- .../Processors/Drawing/DrawImageProcessor.cs | 40 ++- .../Processors/Drawing/FillProcessor.cs | 37 ++- .../Processing/TextGraphicsOptions.cs | 10 +- src/ImageSharp/GraphicsOptions.cs | 24 +- ...erMode.cs => PixelAlphaCompositionMode.cs} | 63 +---- .../PixelFormats/PixelColorBlendingMode.cs | 56 ++++ .../PixelOperations{TPixel}.PixelBenders.cs | 265 ++++++++++++++---- .../Overlays/BackgroundColorProcessor.cs | 2 +- .../Processors/Overlays/GlowProcessor.cs | 2 +- .../Processors/Overlays/VignetteProcessor.cs | 4 +- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 28 +- .../Drawing/FillSolidBrushTests.cs | 68 ++--- .../Drawing/SolidFillBlendedShapesTests.cs | 24 +- .../PorterDuffCompositorTests.cs | 101 ++++--- .../PixelOperationsTests.Blender.cs | 74 ++--- 17 files changed, 513 insertions(+), 291 deletions(-) rename src/ImageSharp/PixelFormats/{PixelBlenderMode.cs => PixelAlphaCompositionMode.cs} (53%) create mode 100644 src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs index 41b47a822..770d44065 100644 --- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing { this.Target = target; this.Options = options; - this.Blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); + this.Blender = PixelOperations.Instance.GetPixelBlender(options); } /// diff --git a/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs index 7c9d7c280..df6080162 100644 --- a/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing /// The blending mode. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float opacity) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode blender, float opacity) where TPixel : struct, IPixel => source.ApplyProcessor(new DrawImageProcessor(image, opacity, blender)); @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing /// The opacity of the image to blend. Must be between 0 and 1. /// The location to draw the blended image. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float opacity, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode blender, float opacity, Point location) where TPixel : struct, IPixel => source.ApplyProcessor(new DrawImageProcessor(image, location, opacity, blender)); diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index faf3fe711..a114daa84 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor(Image image, Point location, float opacity) - : this(image, location, opacity, GraphicsOptions.Default.BlenderMode) + : this(image, location, opacity, GraphicsOptions.Default.ColorBlendingMode) { } @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// Opacity must be between 0 and 1. /// public DrawImageProcessor(Image image, Point location, GraphicsOptions options) - : this(image, location, options.BlendPercentage, options.BlenderMode) + : this(image, location, options.BlendPercentage, options.ColorBlendingMode, options.AlphaCompositionMode) { } @@ -73,9 +73,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The image to blend with the currently processing image. /// The opacity of the image to blend. Must be between 0 and 1. /// The blending mode to use when drawing the image. - public DrawImageProcessor(Image image, float opacity, PixelBlenderMode blenderMode) + public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode blenderMode) : this(image, Point.Empty, opacity, blenderMode) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The Color blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode blenderMode, PixelAlphaCompositionMode alphaMode) + : this(image, Point.Empty, opacity, blenderMode, alphaMode) + { } /// @@ -85,13 +97,31 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. /// The blending mode to use when drawing the image. - public DrawImageProcessor(Image image, Point location, float opacity, PixelBlenderMode blenderMode) + public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode blenderMode) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.Image = image; + this.Opacity = opacity; + this.Blender = PixelOperations.Instance.GetPixelBlender(blenderMode, PixelAlphaCompositionMode.SrcOver); + this.Location = location; + } + + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The location to draw the blended image. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode blenderMode, PixelAlphaCompositionMode alphaMode) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.Image = image; this.Opacity = opacity; - this.Blender = PixelOperations.Instance.GetPixelBlender(blenderMode); + this.Blender = PixelOperations.Instance.GetPixelBlender(blenderMode, alphaMode); this.Location = location; } diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index c9d6777ce..7311a53da 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -102,14 +102,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) { - solidBrush = this.brush as SolidBrush; - - return solidBrush != null - && ((this.options.BlenderMode == PixelBlenderMode.Normal && this.options.BlendPercentage == 1f - && solidBrush.Color.ToVector4().W == 1f) - || (this.options.BlenderMode == PixelBlenderMode.Over && this.options.BlendPercentage == 1f - && solidBrush.Color.ToVector4().W == 1f) - || (this.options.BlenderMode == PixelBlenderMode.Src)); + solidBrush = this.brush as SolidBrush; + + if (solidBrush == null) + { + return false; + } + + if (this.options.ColorBlendingMode != PixelColorBlendingMode.Normal) + { + return false; + } + + if (this.options.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && + this.options.AlphaCompositionMode != PixelAlphaCompositionMode.Src) + { + return false; + } + + if (this.options.BlendPercentage != 1f) + { + return false; + } + + if (solidBrush.Color.ToVector4().W != 1f) + { + return false; + } + + return true; } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs index dfad06768..fa53706a3 100644 --- a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing private float? dpiY; - private PixelBlenderMode blenderMode; + private PixelColorBlendingMode blenderMode; private float wrapTextWidth; @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing this.verticalAlignment = VerticalAlignment.Top; this.antialiasSubpixelDepth = 16; - this.blenderMode = PixelBlenderMode.Normal; + this.blenderMode = PixelColorBlendingMode.Normal; this.blendPercentage = 1; this.antialias = enableAntialiasing; this.dpiX = DefaultTextDpi; @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// - public PixelBlenderMode BlenderMode { get => this.blenderMode; set => this.blenderMode = value; } + public PixelColorBlendingMode BlenderMode { get => this.blenderMode; set => this.blenderMode = value; } /// /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. @@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.Processing { AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, blendPercentage = options.BlendPercentage, - blenderMode = options.BlenderMode + blenderMode = options.ColorBlendingMode }; } @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Processing return new GraphicsOptions(options.Antialias) { AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - BlenderMode = options.BlenderMode, + ColorBlendingMode = options.BlenderMode, BlendPercentage = options.BlendPercentage }; } diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index a094abacb..260fc2d75 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -21,7 +21,9 @@ namespace SixLabors.ImageSharp private bool? antialias; - private PixelBlenderMode blenderMode; + private PixelColorBlendingMode colorBlendingMode; + + private PixelAlphaCompositionMode alphaCompositionMode; /// /// Initializes a new instance of the struct. @@ -29,7 +31,8 @@ namespace SixLabors.ImageSharp /// If set to true [enable antialiasing]. public GraphicsOptions(bool enableAntialiasing) { - this.blenderMode = PixelBlenderMode.Normal; + this.colorBlendingMode = PixelColorBlendingMode.Normal; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.blendPercentage = 1; this.antialiasSubpixelDepth = 16; this.antialias = enableAntialiasing; @@ -67,12 +70,21 @@ namespace SixLabors.ImageSharp // some API thought post V1. /// - /// Gets or sets a value indicating the blending mode to apply to the drawing operation + /// Gets or sets a value indicating the color blending mode to apply to the drawing operation /// - public PixelBlenderMode BlenderMode + public PixelColorBlendingMode ColorBlendingMode { - get => this.blenderMode; - set => this.blenderMode = value; + get => this.colorBlendingMode; + set => this.colorBlendingMode = value; + } + + /// + /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation + /// + public PixelAlphaCompositionMode AlphaCompositionMode + { + get => this.alphaCompositionMode; + set => this.alphaCompositionMode = value; } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs similarity index 53% rename from src/ImageSharp/PixelFormats/PixelBlenderMode.cs rename to src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs index 7a8ab6592..2758a7480 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs @@ -4,55 +4,15 @@ namespace SixLabors.ImageSharp.PixelFormats { /// - /// Enumerates the various blending modes. + /// Enumerates the various alpha composition modes. /// - public enum PixelBlenderMode - { + public enum PixelAlphaCompositionMode + { /// - /// Default blending mode, also known as "Normal" or "Alpha Blending" - /// - Normal = 0, - - /// - /// Blends the 2 values by multiplication. - /// - Multiply, - - /// - /// Blends the 2 values by addition. - /// - Add, - - /// - /// Blends the 2 values by subtraction. - /// - Subtract, - - /// - /// Multiplies the complements of the backdrop and source values, then complements the result. - /// - Screen, - - /// - /// Selects the minimum of the backdrop and source values. - /// - Darken, - - /// - /// Selects the max of the backdrop and source values. - /// - Lighten, - - /// - /// Multiplies or screens the values, depending on the backdrop vector values. - /// - Overlay, - - /// - /// Multiplies or screens the colors, depending on the source value. + /// returns the destination over the source. /// - HardLight, - + SrcOver = 0, + /// /// returns the source colors. /// @@ -61,22 +21,17 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// returns the source over the destination. /// - Atop, - - /// - /// returns the destination over the source. - /// - Over, + SrcAtop, /// /// The source where the destination and source overlap. /// - In, + SrcIn, /// /// The destination where the destination and source overlap. /// - Out, + SrcOut, /// /// The destination where the source does not overlap it. diff --git a/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs b/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs new file mode 100644 index 000000000..a68f7d949 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Enumerates the various color blending modes. + /// + public enum PixelColorBlendingMode + { + /// + /// Default blending mode, also known as "Normal" or "Alpha Blending" + /// + Normal = 0, + + /// + /// Blends the 2 values by multiplication. + /// + Multiply, + + /// + /// Blends the 2 values by addition. + /// + Add, + + /// + /// Blends the 2 values by subtraction. + /// + Subtract, + + /// + /// Multiplies the complements of the backdrop and source values, then complements the result. + /// + Screen, + + /// + /// Selects the minimum of the backdrop and source values. + /// + Darken, + + /// + /// Selects the max of the backdrop and source values. + /// + Lighten, + + /// + /// Multiplies or screens the values, depending on the backdrop vector values. + /// + Overlay, + + /// + /// Multiplies or screens the colors, depending on the source value. + /// + HardLight, + } +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index ca6a28192..dcddadb6a 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -1,50 +1,217 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats.PixelBlenders; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Provides access to pixel blenders - /// - public partial class PixelOperations - where TPixel : struct, IPixel - { - /// - /// Find an instance of the pixel blender. - /// - /// The blending mode to apply - /// A . - internal virtual PixelBlender GetPixelBlender(PixelBlenderMode mode) - { - switch (mode) - { - case PixelBlenderMode.Multiply: return DefaultPixelBlenders.MultiplySrcOver.Instance; - case PixelBlenderMode.Add: return DefaultPixelBlenders.AddSrcOver.Instance; - case PixelBlenderMode.Subtract: return DefaultPixelBlenders.SubtractSrcOver.Instance; - case PixelBlenderMode.Screen: return DefaultPixelBlenders.ScreenSrcOver.Instance; - case PixelBlenderMode.Darken: return DefaultPixelBlenders.DarkenSrcOver.Instance; - case PixelBlenderMode.Lighten: return DefaultPixelBlenders.LightenSrcOver.Instance; - case PixelBlenderMode.Overlay: return DefaultPixelBlenders.OverlaySrcOver.Instance; - case PixelBlenderMode.HardLight: return DefaultPixelBlenders.HardLightSrcOver.Instance; - case PixelBlenderMode.Src: return DefaultPixelBlenders.NormalSrc.Instance; - case PixelBlenderMode.Atop: return DefaultPixelBlenders.NormalSrcAtop.Instance; - case PixelBlenderMode.Over: return DefaultPixelBlenders.NormalSrcOver.Instance; - case PixelBlenderMode.In: return DefaultPixelBlenders.NormalSrcIn.Instance; - case PixelBlenderMode.Out: return DefaultPixelBlenders.NormalSrcOut.Instance; - case PixelBlenderMode.Dest: return DefaultPixelBlenders.NormalDest.Instance; - case PixelBlenderMode.DestAtop: return DefaultPixelBlenders.NormalDestAtop.Instance; - case PixelBlenderMode.DestOver: return DefaultPixelBlenders.NormalDestOver.Instance; - case PixelBlenderMode.DestIn: return DefaultPixelBlenders.NormalDestIn.Instance; - case PixelBlenderMode.DestOut: return DefaultPixelBlenders.NormalDestOut.Instance; - case PixelBlenderMode.Clear: return DefaultPixelBlenders.NormalClear.Instance; - case PixelBlenderMode.Xor: return DefaultPixelBlenders.NormalXor.Instance; - - case PixelBlenderMode.Normal: - default: - return DefaultPixelBlenders.NormalSrcOver.Instance; - } - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides access to pixel blenders + /// + public partial class PixelOperations + where TPixel : struct, IPixel + { + /// + /// Find an instance of the pixel blender. + /// + /// the blending and composition to apply + /// A . + internal PixelBlender GetPixelBlender(GraphicsOptions options) + { + return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); + } + + /// + /// Find an instance of the pixel blender. + /// + /// The color blending mode to apply + /// The alpha composition mode to apply + /// A . + internal virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + { + switch (alphaMode) + { + case PixelAlphaCompositionMode.Clear: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyClear.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddClear.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractClear.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenClear.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenClear.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenClear.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayClear.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightClear.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalClear.Instance; + } + + case PixelAlphaCompositionMode.Xor: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyXor.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddXor.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractXor.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenXor.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenXor.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenXor.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayXor.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightXor.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalXor.Instance; + } + + case PixelAlphaCompositionMode.Src: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrc.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrc.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrc.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrc.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrc.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrc.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrc.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrc.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrc.Instance; + } + + case PixelAlphaCompositionMode.SrcAtop: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcAtop.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcAtop.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcAtop.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcAtop.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcAtop.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcAtop.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcAtop.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcAtop.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcAtop.Instance; + } + + case PixelAlphaCompositionMode.SrcIn: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcIn.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcIn.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcIn.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcIn.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcIn.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcIn.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcIn.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcIn.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcIn.Instance; + } + + case PixelAlphaCompositionMode.SrcOut: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOut.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOut.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOut.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOut.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOut.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOut.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOut.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOut.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcOut.Instance; + } + + case PixelAlphaCompositionMode.Dest: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDest.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDest.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDest.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDest.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDest.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDest.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDest.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDest.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDest.Instance; + } + + case PixelAlphaCompositionMode.DestAtop: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestAtop.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestAtop.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestAtop.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestAtop.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestAtop.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestAtop.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestAtop.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestAtop.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestAtop.Instance; + } + + case PixelAlphaCompositionMode.DestIn: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestIn.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestIn.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestIn.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestIn.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestIn.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestIn.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestIn.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestIn.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestIn.Instance; + } + + case PixelAlphaCompositionMode.DestOut: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOut.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOut.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOut.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOut.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOut.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOut.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOut.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOut.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestOut.Instance; + } + + case PixelAlphaCompositionMode.DestOver: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplyDestOver.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddDestOver.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractDestOver.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenDestOver.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenDestOver.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenDestOver.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlayDestOver.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightDestOver.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalDestOver.Instance; + } + + case PixelAlphaCompositionMode.SrcOver: + default: + switch (colorMode) + { + case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders.MultiplySrcOver.Instance; + case PixelColorBlendingMode.Add: return DefaultPixelBlenders.AddSrcOver.Instance; + case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders.SubtractSrcOver.Instance; + case PixelColorBlendingMode.Screen: return DefaultPixelBlenders.ScreenSrcOver.Instance; + case PixelColorBlendingMode.Darken: return DefaultPixelBlenders.DarkenSrcOver.Instance; + case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders.LightenSrcOver.Instance; + case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders.OverlaySrcOver.Instance; + case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders.HardLightSrcOver.Instance; + case PixelColorBlendingMode.Normal: + default: return DefaultPixelBlenders.NormalSrcOver.Instance; + } + } + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index c7fa2ff19..ecbeebeb0 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays amountSpan[i] = this.GraphicsOptions.BlendPercentage; } - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.GraphicsOptions.BlenderMode); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.GraphicsOptions); ParallelFor.WithConfiguration( minY, maxY, diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 604249331..eb91fec04 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays { this.GlowColor = color; this.Radius = radius; - this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); + this.blender = PixelOperations.Instance.GetPixelBlender(options); this.GraphicsOptions = options; } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index b8bd6c5a5..63780df47 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays { this.VignetteColor = color; this.GraphicsOptions = options; - this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); + this.blender = PixelOperations.Instance.GetPixelBlender(options); } /// @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays this.VignetteColor = color; this.RadiusX = radiusX; this.RadiusY = radiusY; - this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); + this.blender = PixelOperations.Instance.GetPixelBlender(options); this.GraphicsOptions = options; } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 40ad92adc..f50df3b34 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -25,16 +25,16 @@ namespace SixLabors.ImageSharp.Tests }; [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Multiply)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Add)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Subtract)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Screen)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Darken)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Lighten)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Overlay)] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.HardLight)] - public void ImageShouldApplyDrawImage(TestImageProvider provider, PixelBlenderMode mode) + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Multiply)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Add)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Subtract)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Screen)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Darken)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Lighten)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Overlay)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.HardLight)] + public void ImageShouldApplyDrawImage(TestImageProvider provider, PixelColorBlendingMode mode) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -47,8 +47,8 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)] - public void ImageShouldDrawTransformedImage(TestImageProvider provider, PixelBlenderMode mode) + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] + public void ImageShouldDrawTransformedImage(TestImageProvider provider, PixelColorBlendingMode mode) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 backgroundPixel = background[0, 0]; Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; - background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Point(xy, xy))); + background.Mutate(x => x.DrawImage(overlay, PixelColorBlendingMode.Normal, 1F, new Point(xy, xy))); Assert.Equal(Rgba32.White, backgroundPixel); Assert.Equal(overlayPixel, background[0, 0]); @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 backgroundPixel = background[xy - 1, xy - 1]; Rgba32 overlayPixel = overlay[0, 0]; - background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Point(xy, xy))); + background.Mutate(x => x.DrawImage(overlay, PixelColorBlendingMode.Normal, 1F, new Point(xy, xy))); Assert.Equal(Rgba32.White, backgroundPixel); Assert.Equal(overlayPixel, background[xy, xy]); diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index 45f1340be..e86d41f57 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -98,38 +98,38 @@ namespace SixLabors.ImageSharp.Tests.Drawing useReferenceOutputFrom: nameof(this.FillRegion)); } - public static readonly TheoryData BlendData = - new TheoryData() + public static readonly TheoryData BlendData = + new TheoryData() { - { false, "Blue", 0.5f, PixelBlenderMode.Normal, 1.0f }, - { false, "Blue", 1.0f, PixelBlenderMode.Normal, 0.5f }, - { false, "Green", 0.5f, PixelBlenderMode.Normal, 0.3f }, - { false, "HotPink", 0.8f, PixelBlenderMode.Normal, 0.8f }, - - { false, "Blue", 0.5f, PixelBlenderMode.Multiply, 1.0f }, - { false, "Blue", 1.0f, PixelBlenderMode.Multiply, 0.5f }, - { false, "Green", 0.5f, PixelBlenderMode.Multiply, 0.3f }, - { false, "HotPink", 0.8f, PixelBlenderMode.Multiply, 0.8f }, - - { false, "Blue", 0.5f, PixelBlenderMode.Add, 1.0f }, - { false, "Blue", 1.0f, PixelBlenderMode.Add, 0.5f }, - { false, "Green", 0.5f, PixelBlenderMode.Add, 0.3f }, - { false, "HotPink", 0.8f, PixelBlenderMode.Add, 0.8f }, - - { true, "Blue", 0.5f, PixelBlenderMode.Normal, 1.0f }, - { true, "Blue", 1.0f, PixelBlenderMode.Normal, 0.5f }, - { true, "Green", 0.5f, PixelBlenderMode.Normal, 0.3f }, - { true, "HotPink", 0.8f, PixelBlenderMode.Normal, 0.8f }, - - { true, "Blue", 0.5f, PixelBlenderMode.Multiply, 1.0f }, - { true, "Blue", 1.0f, PixelBlenderMode.Multiply, 0.5f }, - { true, "Green", 0.5f, PixelBlenderMode.Multiply, 0.3f }, - { true, "HotPink", 0.8f, PixelBlenderMode.Multiply, 0.8f }, - - { true, "Blue", 0.5f, PixelBlenderMode.Add, 1.0f }, - { true, "Blue", 1.0f, PixelBlenderMode.Add, 0.5f }, - { true, "Green", 0.5f, PixelBlenderMode.Add, 0.3f }, - { true, "HotPink", 0.8f, PixelBlenderMode.Add, 0.8f }, + { false, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, + { false, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, + { false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, + { false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, + + { false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, + { false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, + { false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, + { false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, + + { false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, + { false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, + { false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, + { false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, + + { true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, + { true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, + { true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, + { true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, + + { true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, + { true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, + { true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, + { true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, + + { true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, + { true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, + { true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, + { true, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, }; [Theory] @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing bool triggerFillRegion, string newColorName, float alpha, - PixelBlenderMode blenderMode, + PixelColorBlendingMode blenderMode, float blendPercentage) where TPixel : struct, IPixel { @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing var options = new GraphicsOptions(false) { - BlenderMode = blenderMode, + ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage }; @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(blenderMode); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(blenderMode, PixelAlphaCompositionMode.SrcOver); TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage); image.ComparePixelBufferTo(expectedPixel); diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index b31a18ac4..f6a5b5cdd 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -16,13 +16,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing public class SolidFillBlendedShapesTests { public static IEnumerable modes = - ((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))).Select(x => new object[] { x }); + ((PixelColorBlendingMode[])Enum.GetValues(typeof(PixelColorBlendingMode))).Select(x => new object[] { x }); [Theory] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void _1DarkBlueRect_2BlendHotPinkRect( TestImageProvider provider, - PixelBlenderMode mode) + PixelColorBlendingMode mode) where TPixel : struct, IPixel { using (Image img = provider.GetImage()) @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) ) - .Fill(new GraphicsOptions(true) { BlenderMode = mode }, + .Fill(new GraphicsOptions(true) { ColorBlendingMode = mode }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) ); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse( TestImageProvider provider, - PixelBlenderMode mode) + PixelColorBlendingMode mode) where TPixel : struct, IPixel { using (Image img = provider.GetImage()) @@ -60,12 +60,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { BlenderMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = mode }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { BlenderMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = mode }, NamedColors.Transparent, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse( TestImageProvider provider, - PixelBlenderMode mode) + PixelColorBlendingMode mode) where TPixel : struct, IPixel { using (Image img = provider.GetImage()) @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { BlenderMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = mode }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); var c = NamedColors.Red.ToVector4(); @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing img.Mutate( x => x.Fill( - new GraphicsOptions(true) { BlenderMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = mode }, pixel, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Theory] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendBlackEllipse(TestImageProvider provider, PixelBlenderMode mode) + public void _1DarkBlueRect_2BlendBlackEllipse(TestImageProvider provider, PixelColorBlendingMode mode) where TPixel : struct, IPixel { using(Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) @@ -131,14 +131,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); dstImg.Mutate( - x => x.DrawImage(new GraphicsOptions(true) { BlenderMode = mode }, srcImg) + x => x.DrawImage(new GraphicsOptions(true) { ColorBlendingMode = mode }, srcImg) ); VerifyImage(provider, mode, dstImg); } } - private static void VerifyImage(TestImageProvider provider, PixelBlenderMode mode, Image img) + private static void VerifyImage(TestImageProvider provider, PixelColorBlendingMode mode, Image img) where TPixel : struct, IPixel { img.DebugSave( diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 120619fb5..316a7b216 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -1,47 +1,56 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders -{ - using SixLabors.ImageSharp.PixelFormats; - using SixLabors.ImageSharp.Processing; - - using Xunit; - - public class PorterDuffCompositorTests - { - // TODO: Add other modes to compare. - public static readonly TheoryData CompositingOperators = - new TheoryData - { - PixelBlenderMode.Src, - PixelBlenderMode.Atop, - PixelBlenderMode.Over, - PixelBlenderMode.In, - PixelBlenderMode.Out, - PixelBlenderMode.Dest, - PixelBlenderMode.DestAtop, - PixelBlenderMode.DestOver, - PixelBlenderMode.DestIn, - PixelBlenderMode.DestOut, - PixelBlenderMode.Clear, - PixelBlenderMode.Xor - }; - - [Theory] - [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] - public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelBlenderMode mode) - { - var srcFile = TestFile.Create(TestImages.Png.PDSrc); - using (Image src = srcFile.CreateImage()) - using (Image dest = provider.GetImage()) - { - using (Image res = dest.Clone(x => x.DrawImage(new GraphicsOptions { BlenderMode = mode }, src))) - { - res.DebugSave(provider, mode.ToString()); - res.CompareToReferenceOutput(provider, mode.ToString()); - } - } - } - } +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; + + using Xunit; + + public class PorterDuffCompositorTests + { + // TODO: Add other modes to compare. + public static readonly TheoryData CompositingOperators = + new TheoryData + { + PixelAlphaCompositionMode.Src, + PixelAlphaCompositionMode.SrcAtop, + PixelAlphaCompositionMode.SrcOver, + PixelAlphaCompositionMode.SrcIn, + PixelAlphaCompositionMode.SrcOut, + PixelAlphaCompositionMode.Dest, + PixelAlphaCompositionMode.DestAtop, + PixelAlphaCompositionMode.DestOver, + PixelAlphaCompositionMode.DestIn, + PixelAlphaCompositionMode.DestOut, + PixelAlphaCompositionMode.Clear, + PixelAlphaCompositionMode.Xor + }; + + [Theory] + [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] + public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelAlphaCompositionMode mode) + { + var srcFile = TestFile.Create(TestImages.Png.PDSrc); + using (Image src = srcFile.CreateImage()) + using (Image dest = provider.GetImage()) + { + GraphicsOptions options = new GraphicsOptions + { + AlphaCompositionMode = mode + }; + + using (Image res = dest.Clone(x => x.DrawImage(options, src))) + { + string combinedMode = mode.ToString(); + + if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3); + + res.DebugSave(provider, combinedMode); + res.CompareToReferenceOutput(provider, combinedMode); + } + } + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index 3923a5675..8f574ca16 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -12,64 +12,36 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.PixelFormats { public class PixelBlenderTests - { - - - public static TheoryData BlenderMappings = new TheoryData() + { + public static TheoryData BlenderMappings = new TheoryData() { - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelBlenderMode.Normal }, - { new TestPixel(), typeof(DefaultPixelBlenders.ScreenSrcOver), PixelBlenderMode.Screen }, - { new TestPixel(), typeof(DefaultPixelBlenders.HardLightSrcOver), PixelBlenderMode.HardLight }, - { new TestPixel(), typeof(DefaultPixelBlenders.OverlaySrcOver), PixelBlenderMode.Overlay }, - { new TestPixel(), typeof(DefaultPixelBlenders.DarkenSrcOver), PixelBlenderMode.Darken }, - { new TestPixel(), typeof(DefaultPixelBlenders.LightenSrcOver), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultPixelBlenders.AddSrcOver), PixelBlenderMode.Add }, - { new TestPixel(), typeof(DefaultPixelBlenders.SubtractSrcOver), PixelBlenderMode.Subtract }, - { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelBlenderMode.Multiply }, - - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrc), PixelBlenderMode.Src }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcAtop), PixelBlenderMode.Atop }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelBlenderMode.Over }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcIn), PixelBlenderMode.In }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOut), PixelBlenderMode.Out }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDest), PixelBlenderMode.Dest }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestAtop), PixelBlenderMode.DestAtop }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestOver), PixelBlenderMode.DestOver }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestIn), PixelBlenderMode.DestIn }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestOut), PixelBlenderMode.DestOut }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalClear), PixelBlenderMode.Clear }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalXor), PixelBlenderMode.Xor }, - - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelBlenderMode.Normal }, - { new TestPixel(), typeof(DefaultPixelBlenders.ScreenSrcOver), PixelBlenderMode.Screen }, - { new TestPixel(), typeof(DefaultPixelBlenders.HardLightSrcOver), PixelBlenderMode.HardLight }, - { new TestPixel(), typeof(DefaultPixelBlenders.OverlaySrcOver), PixelBlenderMode.Overlay }, - { new TestPixel(), typeof(DefaultPixelBlenders.DarkenSrcOver), PixelBlenderMode.Darken }, - { new TestPixel(), typeof(DefaultPixelBlenders.LightenSrcOver), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultPixelBlenders.AddSrcOver), PixelBlenderMode.Add }, - { new TestPixel(), typeof(DefaultPixelBlenders.SubtractSrcOver), PixelBlenderMode.Subtract }, - { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelBlenderMode.Multiply }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrc), PixelBlenderMode.Src }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcAtop), PixelBlenderMode.Atop }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelBlenderMode.Over }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcIn), PixelBlenderMode.In }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOut), PixelBlenderMode.Out }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDest), PixelBlenderMode.Dest }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestAtop), PixelBlenderMode.DestAtop }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestOver), PixelBlenderMode.DestOver }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestIn), PixelBlenderMode.DestIn }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalDestOut), PixelBlenderMode.DestOut }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalClear), PixelBlenderMode.Clear }, - { new TestPixel(), typeof(DefaultPixelBlenders.NormalXor), PixelBlenderMode.Xor }, - + { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelColorBlendingMode.Normal }, + { new TestPixel(), typeof(DefaultPixelBlenders.ScreenSrcOver), PixelColorBlendingMode.Screen }, + { new TestPixel(), typeof(DefaultPixelBlenders.HardLightSrcOver), PixelColorBlendingMode.HardLight }, + { new TestPixel(), typeof(DefaultPixelBlenders.OverlaySrcOver), PixelColorBlendingMode.Overlay }, + { new TestPixel(), typeof(DefaultPixelBlenders.DarkenSrcOver), PixelColorBlendingMode.Darken }, + { new TestPixel(), typeof(DefaultPixelBlenders.LightenSrcOver), PixelColorBlendingMode.Lighten }, + { new TestPixel(), typeof(DefaultPixelBlenders.AddSrcOver), PixelColorBlendingMode.Add }, + { new TestPixel(), typeof(DefaultPixelBlenders.SubtractSrcOver), PixelColorBlendingMode.Subtract }, + { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelColorBlendingMode.Multiply }, + + { new TestPixel(), typeof(DefaultPixelBlenders.NormalSrcOver), PixelColorBlendingMode.Normal }, + { new TestPixel(), typeof(DefaultPixelBlenders.ScreenSrcOver), PixelColorBlendingMode.Screen }, + { new TestPixel(), typeof(DefaultPixelBlenders.HardLightSrcOver), PixelColorBlendingMode.HardLight }, + { new TestPixel(), typeof(DefaultPixelBlenders.OverlaySrcOver), PixelColorBlendingMode.Overlay }, + { new TestPixel(), typeof(DefaultPixelBlenders.DarkenSrcOver), PixelColorBlendingMode.Darken }, + { new TestPixel(), typeof(DefaultPixelBlenders.LightenSrcOver), PixelColorBlendingMode.Lighten }, + { new TestPixel(), typeof(DefaultPixelBlenders.AddSrcOver), PixelColorBlendingMode.Add }, + { new TestPixel(), typeof(DefaultPixelBlenders.SubtractSrcOver), PixelColorBlendingMode.Subtract }, + { new TestPixel(), typeof(DefaultPixelBlenders.MultiplySrcOver), PixelColorBlendingMode.Multiply }, }; [Theory] [MemberData(nameof(BlenderMappings))] - public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelBlenderMode mode) + public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelColorBlendingMode mode) where TPixel : struct, IPixel { - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); Assert.IsType(type, blender); } } From 4d53b82e4b3ec8e74669c0570102ca87b1fdbf4a Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Tue, 21 Aug 2018 12:35:35 +0200 Subject: [PATCH 02/10] Fixed missing Alpha Composition property in TextGraphicsOptions --- .../Processing/TextGraphicsOptions.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs index fa53706a3..3c682a761 100644 --- a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs @@ -32,7 +32,9 @@ namespace SixLabors.ImageSharp.Processing private float? dpiY; - private PixelColorBlendingMode blenderMode; + private PixelColorBlendingMode colorBlendingMode; + + private PixelAlphaCompositionMode alphaCompositionMode; private float wrapTextWidth; @@ -53,7 +55,8 @@ namespace SixLabors.ImageSharp.Processing this.verticalAlignment = VerticalAlignment.Top; this.antialiasSubpixelDepth = 16; - this.blenderMode = PixelColorBlendingMode.Normal; + this.colorBlendingMode = PixelColorBlendingMode.Normal; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.blendPercentage = 1; this.antialias = enableAntialiasing; this.dpiX = DefaultTextDpi; @@ -80,9 +83,14 @@ namespace SixLabors.ImageSharp.Processing // some API thought post V1. /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// + public PixelColorBlendingMode ColorBlendingMode { get => this.colorBlendingMode; set => this.colorBlendingMode = value; } + + /// + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation /// - public PixelColorBlendingMode BlenderMode { get => this.blenderMode; set => this.blenderMode = value; } + public PixelAlphaCompositionMode AlphaCompositionMode { get => this.alphaCompositionMode; set => this.alphaCompositionMode = value; } /// /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. @@ -135,7 +143,8 @@ namespace SixLabors.ImageSharp.Processing { AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, blendPercentage = options.BlendPercentage, - blenderMode = options.ColorBlendingMode + colorBlendingMode = options.ColorBlendingMode, + alphaCompositionMode = options.AlphaCompositionMode }; } @@ -151,7 +160,8 @@ namespace SixLabors.ImageSharp.Processing return new GraphicsOptions(options.Antialias) { AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, - ColorBlendingMode = options.BlenderMode, + ColorBlendingMode = options.ColorBlendingMode, + AlphaCompositionMode = options.AlphaCompositionMode, BlendPercentage = options.BlendPercentage }; } From 324cb09452f01a26bac33896c04282b048b08331 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Tue, 21 Aug 2018 15:56:49 +0200 Subject: [PATCH 03/10] refactored DrawImageProcessor methods --- .../Processors/Drawing/DrawImageProcessor.cs | 67 +++++++++---------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index a114daa84..760086183 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -40,6 +40,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public DrawImageProcessor(Image image, GraphicsOptions options) : this(image, Point.Empty, options) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The blending mode to use when drawing the image. + public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode colorBlendingMode) + : this(image, Point.Empty, opacity, colorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The Color blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode) + : this(image, Point.Empty, opacity, colorBlendingMode, alphaCompositionMode) + { } /// @@ -49,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor(Image image, Point location, float opacity) - : this(image, location, opacity, GraphicsOptions.Default.ColorBlendingMode) + : this(image, location, opacity, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode) { } @@ -65,46 +88,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public DrawImageProcessor(Image image, Point location, GraphicsOptions options) : this(image, location, options.BlendPercentage, options.ColorBlendingMode, options.AlphaCompositionMode) { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The blending mode to use when drawing the image. - public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode blenderMode) - : this(image, Point.Empty, opacity, blenderMode) - { } - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The Color blending mode to use when drawing the image. - /// The Alpha blending mode to use when drawing the image. - public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode blenderMode, PixelAlphaCompositionMode alphaMode) - : this(image, Point.Empty, opacity, blenderMode, alphaMode) - { - } - /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. - /// The blending mode to use when drawing the image. - public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode blenderMode) + /// The blending mode to use when drawing the image. + public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode colorBlendingMode) + : this(image, location, opacity, colorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode) { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.Image = image; - this.Opacity = opacity; - this.Blender = PixelOperations.Instance.GetPixelBlender(blenderMode, PixelAlphaCompositionMode.SrcOver); - this.Location = location; } /// @@ -113,15 +108,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. - /// The blending mode to use when drawing the image. - /// The Alpha blending mode to use when drawing the image. - public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode blenderMode, PixelAlphaCompositionMode alphaMode) + /// The blending mode to use when drawing the image. + /// The Alpha blending mode to use when drawing the image. + public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.Image = image; this.Opacity = opacity; - this.Blender = PixelOperations.Instance.GetPixelBlender(blenderMode, alphaMode); + this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Location = location; } From b005d247899cca2cb14d97ac6fb734b4895bf85e Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 22 Aug 2018 09:37:09 +0200 Subject: [PATCH 04/10] rearranged DrawImage method arguments. Fixed tests accordingly --- .../Processing/DrawImageExtensions.cs | 85 +++++++++++------ .../Processors/Drawing/DrawImageProcessor.cs | 93 +------------------ .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 8 +- .../Drawing/SolidFillBlendedShapesTests.cs | 2 +- .../PorterDuffCompositorTests.cs | 2 +- 5 files changed, 68 insertions(+), 122 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs b/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs index df6080162..8ccbe22ac 100644 --- a/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing /// The . public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity) where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, opacity)); + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); /// /// Draws the given image together with the current one by blending their pixels. @@ -30,63 +30,92 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The image to blend with the currently processing image. - /// The blending mode. + /// The blending mode. /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode blender, float opacity) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, float opacity) where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, opacity, blender)); - + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + /// /// Draws the given image together with the current one by blending their pixels. /// /// The pixel format. /// The image this method extends. - /// The options, including the blending type and blending amount. /// The image to blend with the currently processing image. + /// The color blending mode. + /// The alpha composition mode. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, GraphicsOptions options, Image image) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, options)); - + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity)); + /// /// Draws the given image together with the current one by blending their pixels. /// - /// The image this method extends. + /// The pixel format. + /// The image this method extends. /// The image to blend with the currently processing image. + /// The options, including the blending type and blending amount. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); + + /// + /// Draws the given image together with the current one by blending their pixels. + /// /// The pixel format. - /// The opacity of the image to blend. Must be between 0 and 1. + /// The image this method extends. + /// The image to blend with the currently processing image. /// The location to draw the blended image. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, float opacity) where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, opacity)); - + => source.ApplyProcessor(new DrawImageProcessor(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + /// /// 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 to blend. Must be between 0 and 1. + /// The image this method extends. + /// The image to blend with the currently processing image. /// The location to draw the blended image. + /// The color blending to apply. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode blender, float opacity, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, float opacity) where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, opacity, blender)); - + => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); + /// /// Draws the given image together with the current one by blending their pixels. - /// - /// The image this method extends. - /// The options containing the blend mode and opacity. - /// The image to blend with the currently processing image. + /// /// The pixel format. + /// The image this method extends. + /// The image to blend with the currently processing image. /// The location to draw the blended image. + /// The color blending to apply. + /// The alpha composition mode. + /// The opacity of the image to blend. Must be between 0 and 1. + /// The . + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) + where TPixel : struct, IPixel + => source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); + + /// + /// 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 location to draw the blended image. + /// The options containing the blend mode and opacity. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, GraphicsOptions options, Image image, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new DrawImageProcessor(image, location, options)); + => source.ApplyProcessor(new DrawImageProcessor(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 760086183..324d25e09 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -18,99 +18,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// The pixel format. internal class DrawImageProcessor : ImageProcessor where TPixel : struct, IPixel - { + { /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. - public DrawImageProcessor(Image image, float opacity) - : this(image, Point.Empty, opacity) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// - /// The options containing the opacity of the image to blend and blending mode. - /// Opacity must be between 0 and 1. - /// - public DrawImageProcessor(Image image, GraphicsOptions options) - : this(image, Point.Empty, options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The blending mode to use when drawing the image. - public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode colorBlendingMode) - : this(image, Point.Empty, opacity, colorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The Color blending mode to use when drawing the image. - /// The Alpha blending mode to use when drawing the image. - public DrawImageProcessor(Image image, float opacity, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode) - : this(image, Point.Empty, opacity, colorBlendingMode, alphaCompositionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The opacity of the image to blend. Must be between 0 and 1. - public DrawImageProcessor(Image image, Point location, float opacity) - : this(image, location, opacity, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// - /// The options containing the opacity of the image to blend and blending mode. - /// Opacity must be between 0 and 1. - /// - public DrawImageProcessor(Image image, Point location, GraphicsOptions options) - : this(image, location, options.BlendPercentage, options.ColorBlendingMode, options.AlphaCompositionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The blending mode to use when drawing the image. - public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode colorBlendingMode) - : this(image, location, opacity, colorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The location to draw the blended image. - /// The opacity of the image to blend. Must be between 0 and 1. + /// The location to draw the blended image. /// The blending mode to use when drawing the image. - /// The Alpha blending mode to use when drawing the image. - public DrawImageProcessor(Image image, Point location, float opacity, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode) + /// The Alpha blending mode to use when drawing the image. + /// The opacity of the image to blend. Must be between 0 and 1. + public DrawImageProcessor(Image image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index f50df3b34..740b30a8c 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { blend.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); - image.Mutate(x => x.DrawImage(blend, mode, .75f, new Point(image.Width / 4, image.Height / 4))); + image.Mutate(x => x.DrawImage(blend, new Point(image.Width / 4, image.Height / 4), mode, .75f) ); image.DebugSave(provider, new { mode }); } } @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests new Rectangle(0, 0, destBounds.Width, destBounds.Height))); var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); - image.Mutate(x => x.DrawImage(blend, mode, .75F, position)); + image.Mutate(x => x.DrawImage(blend, position, mode, .75F)); image.DebugSave(provider, new[] { "Transformed" }); } } @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 backgroundPixel = background[0, 0]; Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; - background.Mutate(x => x.DrawImage(overlay, PixelColorBlendingMode.Normal, 1F, new Point(xy, xy))); + background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); Assert.Equal(Rgba32.White, backgroundPixel); Assert.Equal(overlayPixel, background[0, 0]); @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 backgroundPixel = background[xy - 1, xy - 1]; Rgba32 overlayPixel = overlay[0, 0]; - background.Mutate(x => x.DrawImage(overlay, PixelColorBlendingMode.Normal, 1F, new Point(xy, xy))); + background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); Assert.Equal(Rgba32.White, backgroundPixel); Assert.Equal(overlayPixel, background[xy, xy]); diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index f6a5b5cdd..6c0efce7a 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); dstImg.Mutate( - x => x.DrawImage(new GraphicsOptions(true) { ColorBlendingMode = mode }, srcImg) + x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = mode }) ); VerifyImage(provider, mode, dstImg); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 316a7b216..9c3ea90d5 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders AlphaCompositionMode = mode }; - using (Image res = dest.Clone(x => x.DrawImage(options, src))) + using (Image res = dest.Clone(x => x.DrawImage(src, options))) { string combinedMode = mode.ToString(); From 20daca0e6e296c6aa46d751f8937d0d6f38b4850 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 22 Aug 2018 12:18:38 +0200 Subject: [PATCH 05/10] Updated SolidFill tests to cover all possible Blending-Composition combinations, reference images not in place yet --- .../Drawing/SolidFillBlendedShapesTests.cs | 72 ++++++++++++------- .../TestUtilities/TestImageExtensions.cs | 2 +- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index 6c0efce7a..3686cceb6 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -15,14 +15,26 @@ namespace SixLabors.ImageSharp.Tests.Drawing [GroupOutput("Drawing")] public class SolidFillBlendedShapesTests { - public static IEnumerable modes = - ((PixelColorBlendingMode[])Enum.GetValues(typeof(PixelColorBlendingMode))).Select(x => new object[] { x }); + public static IEnumerable modes = GetAllModeCombinations(); + + private static IEnumerable GetAllModeCombinations() + { + foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) + { + foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) + { + yield return new object[] { blending, composition }; + } + } + } + [Theory] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void _1DarkBlueRect_2BlendHotPinkRect( TestImageProvider provider, - PixelColorBlendingMode mode) + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) where TPixel : struct, IPixel { using (Image img = provider.GetImage()) @@ -34,12 +46,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) ) - .Fill(new GraphicsOptions(true) { ColorBlendingMode = mode }, + .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) ); - VerifyImage(provider, mode, img); + VerifyImage(provider, blending, composition, img); } } @@ -47,7 +59,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse( TestImageProvider provider, - PixelColorBlendingMode mode) + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) where TPixel : struct, IPixel { using (Image img = provider.GetImage()) @@ -60,17 +73,17 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = blending }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = blending }, NamedColors.Transparent, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); - VerifyImage(provider, mode, img); + VerifyImage(provider, blending, composition, img); } } @@ -78,7 +91,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse( TestImageProvider provider, - PixelColorBlendingMode mode) + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) where TPixel : struct, IPixel { using (Image img = provider.GetImage()) @@ -91,7 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = blending }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); var c = NamedColors.Red.ToVector4(); @@ -101,18 +115,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = mode }, + new GraphicsOptions(true) { ColorBlendingMode = blending }, pixel, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); - VerifyImage(provider, mode, img); ; + VerifyImage(provider, blending, composition, img); ; } } [Theory] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] - public void _1DarkBlueRect_2BlendBlackEllipse(TestImageProvider provider, PixelColorBlendingMode mode) + public void _1DarkBlueRect_2BlendBlackEllipse( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition) where TPixel : struct, IPixel { using(Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) @@ -131,28 +148,35 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); dstImg.Mutate( - x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = mode }) + x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending }) ); - VerifyImage(provider, mode, dstImg); + VerifyImage(provider, blending, composition, dstImg); } } - private static void VerifyImage(TestImageProvider provider, PixelColorBlendingMode mode, Image img) + private static void VerifyImage(TestImageProvider provider, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, Image img) where TPixel : struct, IPixel { img.DebugSave( provider, - new { mode }, + new { blending }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - var comparer = ImageComparer.TolerantPercentage(0.01f, 3); - img.CompareFirstFrameToReferenceOutput(comparer, - provider, - new { mode }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); + try + { + var comparer = ImageComparer.TolerantPercentage(0.01f, 3); + img.CompareFirstFrameToReferenceOutput(comparer, + provider, + new { blending }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + catch(System.IO.FileNotFoundException) + { + // temporary hack until reference images are in place. + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 688048663..a93587367 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -365,7 +365,7 @@ namespace SixLabors.ImageSharp.Tests if (!File.Exists(referenceOutputFile)) { - throw new Exception("Reference output file missing: " + referenceOutputFile); + throw new System.IO.FileNotFoundException("Reference output file missing: " + referenceOutputFile, referenceOutputFile); } IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); From 05a2a4bd2be3d92c31d8bae03aa3fb78ab22a1ce Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 22 Aug 2018 13:20:15 +0200 Subject: [PATCH 06/10] Fixing SolidFill tests again... --- .../Drawing/SolidFillBlendedShapesTests.cs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index 3686cceb6..3a572e29d 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -21,10 +21,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing { foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) { + // until reference images are in place, we will only test SrcOver + yield return new object[] { blending, PixelAlphaCompositionMode.SrcOver }; + + /* foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) { yield return new object[] { blending, composition }; - } + }*/ } } @@ -46,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) ) - .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending }, + .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) ); @@ -73,12 +77,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending }, + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending }, + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, NamedColors.Transparent, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -105,7 +109,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending }, + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); var c = NamedColors.Red.ToVector4(); @@ -115,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending }, + new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, pixel, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -148,14 +152,18 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); dstImg.Mutate( - x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending }) + x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) ); VerifyImage(provider, blending, composition, dstImg); } } - private static void VerifyImage(TestImageProvider provider, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, Image img) + private static void VerifyImage( + TestImageProvider provider, + PixelColorBlendingMode blending, + PixelAlphaCompositionMode composition, + Image img) where TPixel : struct, IPixel { img.DebugSave( @@ -163,20 +171,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing new { blending }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - - try - { - var comparer = ImageComparer.TolerantPercentage(0.01f, 3); - img.CompareFirstFrameToReferenceOutput(comparer, - provider, - new { blending }, - appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); - } - catch(System.IO.FileNotFoundException) - { - // temporary hack until reference images are in place. - } + + var comparer = ImageComparer.TolerantPercentage(0.01f, 3); + img.CompareFirstFrameToReferenceOutput(comparer, + provider, + new { blending }, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); } } } \ No newline at end of file From fa60a3215b8a900ddd6cb43e941c6b59f41d2315 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 22 Aug 2018 14:57:45 +0200 Subject: [PATCH 07/10] fixed argument name to help reflection match the file names --- .../ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index 3a572e29d..92cfc2434 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -161,21 +161,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing private static void VerifyImage( TestImageProvider provider, - PixelColorBlendingMode blending, + PixelColorBlendingMode mode, PixelAlphaCompositionMode composition, Image img) where TPixel : struct, IPixel { img.DebugSave( provider, - new { blending }, + new { mode }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); var comparer = ImageComparer.TolerantPercentage(0.01f, 3); img.CompareFirstFrameToReferenceOutput(comparer, provider, - new { blending }, + new { mode }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } From 3eb0a73081aff44112b4a225d2ecc5638d3761fa Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 22 Aug 2018 16:26:52 +0200 Subject: [PATCH 08/10] Refactored IsSolidBrushWithoutBlending into GraphicsOptions so it can be called from more places, and also allows for specific tests. --- .../Processors/Drawing/FillProcessor.cs | 23 +---- src/ImageSharp/GraphicsOptions.cs | 89 +++++++++++++++++++ .../PixelFormats/PixelOperationsTests.cs | 11 +++ 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index 7311a53da..3285e75a7 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -109,28 +109,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing return false; } - if (this.options.ColorBlendingMode != PixelColorBlendingMode.Normal) - { - return false; - } - - if (this.options.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && - this.options.AlphaCompositionMode != PixelAlphaCompositionMode.Src) - { - return false; - } - - if (this.options.BlendPercentage != 1f) - { - return false; - } - - if (solidBrush.Color.ToVector4().W != 1f) - { - return false; - } - - return true; + return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color); } } } \ No newline at end of file diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index 260fc2d75..2d20c1773 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -36,6 +36,57 @@ namespace SixLabors.ImageSharp this.blendPercentage = 1; this.antialiasSubpixelDepth = 16; this.antialias = enableAntialiasing; + } + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + /// blending percentage to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.colorBlendingMode = PixelColorBlendingMode.Normal; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + /// blending percentage to apply to the drawing operation + /// color blending mode to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.colorBlendingMode = blending; + this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + /// blending percentage to apply to the drawing operation + /// color blending mode to apply to the drawing operation + /// alpha composition mode to apply to the drawing operation + public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.colorBlendingMode = blending; + this.alphaCompositionMode = composition; + this.blendPercentage = opacity; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; } /// @@ -85,6 +136,44 @@ namespace SixLabors.ImageSharp { get => this.alphaCompositionMode; set => this.alphaCompositionMode = value; + } + + /// + /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. + /// + /// The pixel format + /// the color + /// true if the color can be considered opaque + /// + /// Blending and composition is an expensive operation, in some cases, like + /// filling with a solid color, the blending can be avoided by a plain color replacement. + /// This method can be useful for such processors to select the fast path. + /// + internal bool IsOpaqueColorWithoutBlending(TPixel color) + where TPixel : struct, IPixel + { + if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) + { + return false; + } + + if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && + this.AlphaCompositionMode != PixelAlphaCompositionMode.Src) + { + return false; + } + + if (this.BlendPercentage != 1f) + { + return false; + } + + if (color.ToVector4().W != 1f) + { + return false; + } + + return true; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index e084379ba..9e41fd94f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -88,6 +88,17 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats where TPixel : struct, IPixel { Assert.NotNull(PixelOperations.Instance); + } + + [Fact] + public void IsOpaqueColor() + { + Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); + + Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); + Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Transparent)); + Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); + Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal,PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); } } From 458fe254dc2beac9888afc0a35e0e8a7fcc8def6 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 23 Aug 2018 12:26:24 +0200 Subject: [PATCH 09/10] SolidFillBlendedShapesTests now tests all composition/blending combinations. --- .../Drawing/SolidFillBlendedShapesTests.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index 92cfc2434..a8fb187ce 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -19,16 +19,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing private static IEnumerable GetAllModeCombinations() { - foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) + foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) { - // until reference images are in place, we will only test SrcOver - yield return new object[] { blending, PixelAlphaCompositionMode.SrcOver }; - - /* - foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) + foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) { yield return new object[] { blending, composition }; - }*/ + } } } @@ -161,21 +157,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing private static void VerifyImage( TestImageProvider provider, - PixelColorBlendingMode mode, + PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, Image img) where TPixel : struct, IPixel { img.DebugSave( provider, - new { mode }, + new { composition, blending }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); var comparer = ImageComparer.TolerantPercentage(0.01f, 3); img.CompareFirstFrameToReferenceOutput(comparer, provider, - new { mode }, + new { composition, blending }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); } From 374e939e0a07382741d934e86a91af21ab7f960d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 23 Aug 2018 19:18:36 +0100 Subject: [PATCH 10/10] Update reference images --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index 825220cdc..6a43d335f 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 825220cdc4e9d1b4b3b474c63139e18e1cdb800e +Subproject commit 6a43d335f216d6325a6a9fd8d35942ade12b7c7b