From a3a69824dd8c6ac44847794ad219a97fede7db98 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 28 Apr 2017 21:13:29 +0200 Subject: [PATCH 1/7] Added Pixel effect mode enumeration for Porter-Duff composition modes --- .../PixelFormats/PixelTransformMode.cs | 58 ++++ .../PixelTransformModeExtensions.cs | 56 ++++ .../PixelFormats/PorterDuffFunctions.cs | 267 ++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 src/ImageSharp/PixelFormats/PixelTransformMode.cs create mode 100644 src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs create mode 100644 src/ImageSharp/PixelFormats/PorterDuffFunctions.cs diff --git a/src/ImageSharp/PixelFormats/PixelTransformMode.cs b/src/ImageSharp/PixelFormats/PixelTransformMode.cs new file mode 100644 index 0000000000..7ac7eb0299 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelTransformMode.cs @@ -0,0 +1,58 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + /// + /// Porter Duff Blending composition modes + /// + public enum PixelTransformMode + { + /// + /// Default blending mode, also known as "Normal" or "Alpha Blending" + /// + Normal, + + /// + /// Backdrop + Source + /// + Multiply, + + /// + /// Backdrop + Source + /// + Add, + + /// + /// Backdrop - Source + /// + Substract, + + /// + /// Screen effect + /// + Screen, + + /// + /// Darken effect + /// + Darken, + + /// + /// Lighten effect + /// + Lighten, + + /// + /// Overlay effect + /// + Overlay, + + /// + /// Hard light effect + /// + HardLight + } +} diff --git a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs new file mode 100644 index 0000000000..1992244620 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Collections.Generic; + using System.Text; + using PixelFormats; + + /// + /// Extensions to retrieve the appropiate pixel transformation functions for + /// + public static class PixelTransformModeExtensions + { + /// + /// Gets a pixel transformation function + /// + /// The pixel format used for both Backdrop and Source + /// The Duff Porter mode + /// A function that transforms a Backdrop and Source colors into a final color + public static Func GetPixelFunction(this PixelTransformMode mode) + where TPixel : IPixel + { + return mode.GetPixelFunction(); + } + + /// + /// Gets a pixel transformation function + /// + /// The pixel format used for Backdrop and Output + /// The pixel format used for Source + /// The Duff Porter mode + /// A function that transforms a Backdrop and Source colors into a final color + public static Func GetPixelFunction(this PixelTransformMode mode) + where TBckPixel : IPixel + where TSrcPixel : IPixel + { + switch (mode) + { + case PixelTransformMode.Normal: return PorterDuffFunctions.NormalBlendFunction; + case PixelTransformMode.Multiply: return PorterDuffFunctions.MultiplyFunction; + case PixelTransformMode.Add: return PorterDuffFunctions.AddFunction; + case PixelTransformMode.Screen: return PorterDuffFunctions.ScreenFunction; + case PixelTransformMode.Darken: return PorterDuffFunctions.DarkenFunction; + case PixelTransformMode.Lighten: return PorterDuffFunctions.LightenFunction; + case PixelTransformMode.Overlay: return PorterDuffFunctions.OverlayFunction; + case PixelTransformMode.HardLight: return PorterDuffFunctions.HardLightFunction; + + default: throw new NotImplementedException(nameof(mode)); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs new file mode 100644 index 0000000000..0e9b985e81 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs @@ -0,0 +1,267 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Collection of Porter Duff alpha blending functions + /// + /// Backdrop Pixel Format + /// Source Pixel Format + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class PorterDuffFunctions + where TBckPixel : IPixel + where TsrcPixel : IPixel + { + /// + /// Source over backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel NormalBlendFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, l); + } + + /// + /// Source multiplied by backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel MultiplyFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, b * l); + } + + /// + /// Source added to backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel AddFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, Vector4.Min(Vector4.One, b + l)); + } + + /// + /// Source substracted from backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel SubstractFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, Vector4.Max(Vector4.Zero, b - l)); + } + + /// + /// Complement of source multiplied by the complement of backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel ScreenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, Vector4.One - ((Vector4.One - b) * (Vector4.One - l))); + } + + /// + /// Per element, chooses the smallest value of source and backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel DarkenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, Vector4.Min(b, l)); + } + + /// + /// Per element, chooses the largest value of source and backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel LightenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + return Compose(b, l, Vector4.Max(b, l)); + } + + /// + /// Overlays source over backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel OverlayFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + float cr = OverlayValueFunction(b.X, l.X); + float cg = OverlayValueFunction(b.Y, l.Y); + float cb = OverlayValueFunction(b.Z, l.Z); + + return Compose(b, l, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); + } + + /// + /// Hard light effect + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + public static TBckPixel HardLightFunction(TBckPixel backdrop, TsrcPixel source, float opacity) + { + Vector4 l = source.ToVector4(); + l.W *= opacity; + if (l.W == 0) + { + return backdrop; + } + + Vector4 b = backdrop.ToVector4(); + + float cr = OverlayValueFunction(l.X, b.X); + float cg = OverlayValueFunction(l.Y, b.Y); + float cb = OverlayValueFunction(l.Z, b.Z); + + return Compose(b, l, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); + } + + /// + /// Helper function for Overlay andHardLight modes + /// + /// Backdrop color element + /// Source color element + /// Overlay value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float OverlayValueFunction(float backdrop, float source) + { + return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); + } + + /// + /// General composition function for all modes, with a general solution for alpha channel + /// + /// Original backgrop color + /// Original source color + /// Desired transformed color, without taking Alpha channel in account + /// The final color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) + { + System.Diagnostics.Debug.Assert(source.W > 0, nameof(source) + " Alpha must be non zero"); + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; + xform.W = a; + + TBckPixel packed = default(TBckPixel); + packed.PackFromVector4(xform); + + return packed; + } + } +} \ No newline at end of file From bbaa8898e5fa26b03438ab683d0cbfe5007d4c0e Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 28 Apr 2017 21:14:13 +0200 Subject: [PATCH 2/7] Added Image drawing functions to use general porter duff modes --- src/ImageSharp.Drawing/DrawImage.cs | 50 ++++++++- .../Processors/DrawImageEffectProcessor.cs | 102 ++++++++++++++++++ 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 6a4f49337c..a7f2bf7011 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -4,7 +4,8 @@ // namespace ImageSharp -{ +{ + using System; using Drawing.Processors; using ImageSharp.PixelFormats; @@ -52,6 +53,53 @@ namespace ImageSharp source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds); return source; + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The image this method extends. + /// The image to blend with the currently processing image. + /// Pixel function effect to apply on every pixel + /// The pixel format. + /// The opacity of the image image to blend. Must be between 0 and 100. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, PixelTransformMode mode, int percent, Size size, Point location) + where TPixel : struct, IPixel + { + Func pixelFunc = mode.GetPixelFunction(); + + return DrawImage(source, image, pixelFunc, percent, size, location); + } + + /// + /// 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. + /// Pixel function effect to apply on every pixel + /// The pixel format. + /// The opacity of the image image to blend. Must be between 0 and 100. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, Func pixelFunc, int percent, Size size, Point location) + where TPixel : struct, IPixel + { + if (size == default(Size)) + { + size = new Size(image.Width, image.Height); + } + + if (location == default(Point)) + { + location = Point.Empty; + } + + source.ApplyProcessor(new DrawImageEffectProcessor(image, size, location, pixelFunc, percent), source.Bounds); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs new file mode 100644 index 0000000000..4b57be53ec --- /dev/null +++ b/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs @@ -0,0 +1,102 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Processors +{ + using System; + using System.Numerics; + using System.Threading.Tasks; + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + /// + /// Combines two images together by blending the pixels. + /// + /// The pixel format. + internal class DrawImageEffectProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// Pixel function effect to apply on every pixel + /// The opacity of the image to blend. Between 0 and 100. + public DrawImageEffectProcessor(Image image, Size size, Point location, Func pixelFunction, int alpha = 100) + { + Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); + this.Image = image; + this.PixelFunction = pixelFunction; + this.Size = size; + this.Location = location; + this.Alpha = alpha; + } + + /// + /// Gets the image to blend. + /// + public Image Image { get; private set; } + + /// + /// Gets The function effect to apply on a per pixel basis + /// + public Func PixelFunction { get; private set; } + + /// + /// Gets the alpha percentage value. + /// + public int Alpha { get; } + + /// + /// Gets the size to draw the blended image. + /// + public Size Size { get; } + + /// + /// Gets the location to draw the blended image. + /// + public Point Location { get; } + + /// + protected override void OnApply(ImageBase target, Rectangle sourceRectangle) + { + if (this.Image.Bounds.Size != this.Size) + { + // should Resize be moved to core? + this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); + } + + // Align start/end positions. + Rectangle bounds = this.Image.Bounds; + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + + float alpha = this.Alpha / 100F; + + using (PixelAccessor sourcePixels = this.Image.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + for (int x = minX; x < maxX; x++) + { + TPixel targetColor = targetPixels[x, y]; + TPixel sourceColor = sourcePixels[x - minX, y - minY]; + + targetPixels[x, y] = this.PixelFunction(targetColor, sourceColor, alpha); + } + }); + } + } + } +} \ No newline at end of file From c442cb445aedab0d23af2a5ecc0b10132e983a2d Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 28 Apr 2017 22:28:46 +0200 Subject: [PATCH 3/7] fixed stylecop --- src/ImageSharp.Drawing/DrawImage.cs | 188 +++++++++--------- .../Processors/DrawImageEffectProcessor.cs | 174 ++++++++-------- 2 files changed, 181 insertions(+), 181 deletions(-) diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index a7f2bf7011..52c275595b 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -1,105 +1,105 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp { using System; - using Drawing.Processors; - using ImageSharp.PixelFormats; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The . - public static Image Blend(this Image source, Image image, int percent = 50) - where TPixel : struct, IPixel - { - return DrawImage(source, image, percent, default(Size), default(Point)); - } - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The size to draw the blended image. - /// The location to draw the blended image. - /// The . - public static Image DrawImage(this Image source, Image image, int percent, Size size, Point location) - where TPixel : struct, IPixel - { - if (size == default(Size)) - { - size = new Size(image.Width, image.Height); - } - - if (location == default(Point)) - { - location = Point.Empty; - } - - source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds); - return source; + using Drawing.Processors; + using ImageSharp.PixelFormats; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The opacity of the image image to blend. Must be between 0 and 100. + /// The . + public static Image Blend(this Image source, Image image, int percent = 50) + where TPixel : struct, IPixel + { + return DrawImage(source, image, percent, default(Size), default(Point)); } - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The image this method extends. + /// + /// 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. - /// Pixel function effect to apply on every pixel - /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The size to draw the blended image. - /// The location to draw the blended image. - /// The . - public static Image DrawImage(this Image source, Image image, PixelTransformMode mode, int percent, Size size, Point location) - where TPixel : struct, IPixel - { + /// The pixel format. + /// The opacity of the image image to blend. Must be between 0 and 100. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, int percent, Size size, Point location) + where TPixel : struct, IPixel + { + if (size == default(Size)) + { + size = new Size(image.Width, image.Height); + } + + if (location == default(Point)) + { + location = Point.Empty; + } + + source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds); + return source; + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The image this method extends. + /// The image to blend with the currently processing image. + /// Pixel function effect to apply on every pixel + /// The pixel format. + /// The opacity of the image image to blend. Must be between 0 and 100. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, PixelTransformMode mode, int percent, Size size, Point location) + where TPixel : struct, IPixel + { Func pixelFunc = mode.GetPixelFunction(); - return DrawImage(source, image, pixelFunc, percent, size, location); + return DrawImage(source, image, pixelFunc, percent, size, location); } - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The image this method extends. + /// + /// 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. - /// Pixel function effect to apply on every pixel - /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The size to draw the blended image. - /// The location to draw the blended image. - /// The . - public static Image DrawImage(this Image source, Image image, Func pixelFunc, int percent, Size size, Point location) - where TPixel : struct, IPixel - { - if (size == default(Size)) - { - size = new Size(image.Width, image.Height); - } - - if (location == default(Point)) - { - location = Point.Empty; - } - - source.ApplyProcessor(new DrawImageEffectProcessor(image, size, location, pixelFunc, percent), source.Bounds); - return source; - } - } + /// Pixel function effect to apply on every pixel + /// The pixel format. + /// The opacity of the image image to blend. Must be between 0 and 100. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, Func pixelFunc, int percent, Size size, Point location) + where TPixel : struct, IPixel + { + if (size == default(Size)) + { + size = new Size(image.Width, image.Height); + } + + if (location == default(Point)) + { + location = Point.Empty; + } + + source.ApplyProcessor(new DrawImageEffectProcessor(image, size, location, pixelFunc, percent), source.Bounds); + return source; + } + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs index 4b57be53ec..4ef8800f92 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageEffectProcessor.cs @@ -1,102 +1,102 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - /// - /// Combines two images together by blending the pixels. - /// - /// The pixel format. - internal class DrawImageEffectProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. - /// The size to draw the blended image. +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Processors +{ + using System; + using System.Numerics; + using System.Threading.Tasks; + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + /// + /// Combines two images together by blending the pixels. + /// + /// The pixel format. + internal class DrawImageEffectProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. + /// The size to draw the blended image. /// The location to draw the blended image. /// Pixel function effect to apply on every pixel - /// The opacity of the image to blend. Between 0 and 100. - public DrawImageEffectProcessor(Image image, Size size, Point location, Func pixelFunction, int alpha = 100) + /// The opacity of the image to blend. Between 0 and 100. + public DrawImageEffectProcessor(Image image, Size size, Point location, Func pixelFunction, int alpha = 100) { - Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); + Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); this.Image = image; - this.PixelFunction = pixelFunction; - this.Size = size; + this.PixelFunction = pixelFunction; + this.Size = size; this.Location = location; - this.Alpha = alpha; - } - - /// - /// Gets the image to blend. - /// + this.Alpha = alpha; + } + + /// + /// Gets the image to blend. + /// public Image Image { get; private set; } /// /// Gets The function effect to apply on a per pixel basis /// - public Func PixelFunction { get; private set; } - - /// - /// Gets the alpha percentage value. - /// - public int Alpha { get; } - - /// - /// Gets the size to draw the blended image. - /// - public Size Size { get; } - - /// - /// Gets the location to draw the blended image. - /// - public Point Location { get; } - - /// - protected override void OnApply(ImageBase target, Rectangle sourceRectangle) - { - if (this.Image.Bounds.Size != this.Size) - { - // should Resize be moved to core? - this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); - } - - // Align start/end positions. - Rectangle bounds = this.Image.Bounds; - int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); - int minY = Math.Max(this.Location.Y, sourceRectangle.Y); - int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - - float alpha = this.Alpha / 100F; - - using (PixelAccessor sourcePixels = this.Image.Lock()) - using (PixelAccessor targetPixels = target.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) + public Func PixelFunction { get; private set; } + + /// + /// Gets the alpha percentage value. + /// + public int Alpha { get; } + + /// + /// Gets the size to draw the blended image. + /// + public Size Size { get; } + + /// + /// Gets the location to draw the blended image. + /// + public Point Location { get; } + + /// + protected override void OnApply(ImageBase target, Rectangle sourceRectangle) + { + if (this.Image.Bounds.Size != this.Size) + { + // should Resize be moved to core? + this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); + } + + // Align start/end positions. + Rectangle bounds = this.Image.Bounds; + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + + float alpha = this.Alpha / 100F; + + using (PixelAccessor sourcePixels = this.Image.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + for (int x = minX; x < maxX; x++) { TPixel targetColor = targetPixels[x, y]; TPixel sourceColor = sourcePixels[x - minX, y - minY]; - targetPixels[x, y] = this.PixelFunction(targetColor, sourceColor, alpha); - } - }); - } - } - } + targetPixels[x, y] = this.PixelFunction(targetColor, sourceColor, alpha); + } + }); + } + } + } } \ No newline at end of file From 3b100bb979d54c98bac234787a989bf282ee37b0 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 28 Apr 2017 22:29:09 +0200 Subject: [PATCH 4/7] fixed headers --- src/ImageSharp/PixelFormats/PixelTransformMode.cs | 2 +- src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs | 2 +- src/ImageSharp/PixelFormats/PorterDuffFunctions.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelTransformMode.cs b/src/ImageSharp/PixelFormats/PixelTransformMode.cs index 7ac7eb0299..159b275158 100644 --- a/src/ImageSharp/PixelFormats/PixelTransformMode.cs +++ b/src/ImageSharp/PixelFormats/PixelTransformMode.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs index 1992244620..50cda63827 100644 --- a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs +++ b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs index 0e9b985e81..f3717f8f8f 100644 --- a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // From 106e93626f5f6413e0b3a823c391c89068c0a472 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 28 Apr 2017 23:36:52 +0200 Subject: [PATCH 5/7] Replaced assert with DebugGuard --- src/ImageSharp/PixelFormats/PorterDuffFunctions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs index f3717f8f8f..1c9f6b8425 100644 --- a/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PorterDuffFunctions.cs @@ -244,7 +244,7 @@ namespace ImageSharp.PixelFormats [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) { - System.Diagnostics.Debug.Assert(source.W > 0, nameof(source) + " Alpha must be non zero"); + DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); // calculate weights float xw = backdrop.W * source.W; From 8742a3ccff7f739d7df16ab72376ad9b009df751 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Sat, 29 Apr 2017 00:24:59 +0200 Subject: [PATCH 6/7] Added missing function --- src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs index 50cda63827..4ecddfded0 100644 --- a/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs +++ b/src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs @@ -43,6 +43,7 @@ namespace ImageSharp case PixelTransformMode.Normal: return PorterDuffFunctions.NormalBlendFunction; case PixelTransformMode.Multiply: return PorterDuffFunctions.MultiplyFunction; case PixelTransformMode.Add: return PorterDuffFunctions.AddFunction; + case PixelTransformMode.Substract: return PorterDuffFunctions.SubstractFunction; case PixelTransformMode.Screen: return PorterDuffFunctions.ScreenFunction; case PixelTransformMode.Darken: return PorterDuffFunctions.DarkenFunction; case PixelTransformMode.Lighten: return PorterDuffFunctions.LightenFunction; From 3a542eec74dd3f4bfa3b1a63018db6c98bccc31a Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Sat, 29 Apr 2017 00:25:28 +0200 Subject: [PATCH 7/7] Added a DrawImage Effect test --- .../Drawing/DrawImageEffectTest.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs new file mode 100644 index 0000000000..78f0a08702 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using ImageSharp.PixelFormats; + using Xunit; + + public class DrawImageEffectTest : FileTestBase + { + [Fact] + public void ImageShouldApplyDrawImageFilter() + { + string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect"); + + PixelTransformMode[] modes = (PixelTransformMode[])System.Enum.GetValues(typeof(PixelTransformMode)); + + using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage()) + { + foreach (TestFile file in Files) + { + using (Image image = file.CreateImage()) + { + foreach (PixelTransformMode mode in modes) + { + using (FileStream output = File.OpenWrite($"{path}/{mode}.{file.FileName}")) + { + Size size = new Size(image.Width / 2, image.Height / 2); + Point loc = new Point(image.Width / 4, image.Height / 4); + + image.DrawImage(blend, mode, 75, size, loc).Save(output); + } + } + } + } + } + } + } +} \ No newline at end of file