mirror of https://github.com/SixLabors/ImageSharp
6 changed files with 630 additions and 56 deletions
@ -1,57 +1,105 @@ |
|||
// <copyright file="DrawImage.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using Drawing.Processors; |
|||
using ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, int percent = 50) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return DrawImage(source, image, percent, default(Size), default(Point)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, int percent, Size size, Point location) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (size == default(Size)) |
|||
{ |
|||
size = new Size(image.Width, image.Height); |
|||
} |
|||
|
|||
if (location == default(Point)) |
|||
{ |
|||
location = Point.Empty; |
|||
} |
|||
|
|||
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, percent), source.Bounds); |
|||
return source; |
|||
} |
|||
} |
|||
// <copyright file="DrawImage.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using Drawing.Processors; |
|||
using ImageSharp.PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Extension methods for the <see cref="Image"/> type.
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, int percent = 50) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return DrawImage(source, image, percent, default(Size), default(Point)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, int percent, Size size, Point location) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (size == default(Size)) |
|||
{ |
|||
size = new Size(image.Width, image.Height); |
|||
} |
|||
|
|||
if (location == default(Point)) |
|||
{ |
|||
location = Point.Empty; |
|||
} |
|||
|
|||
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, percent), source.Bounds); |
|||
return source; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <param name="mode">Pixel function effect to apply on every pixel</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelTransformMode mode, int percent, Size size, Point location) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
Func<TPixel, TPixel, float, TPixel> pixelFunc = mode.GetPixelFunction<TPixel>(); |
|||
|
|||
return DrawImage(source, image, pixelFunc, percent, size, location); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Draws the given image together with the current one by blending their pixels.
|
|||
/// </summary>
|
|||
/// <param name="source">The image this method extends.</param>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <param name="pixelFunc">Pixel function effect to apply on every pixel</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
|||
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, Func<TPixel, TPixel, float, TPixel> pixelFunc, int percent, Size size, Point location) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
if (size == default(Size)) |
|||
{ |
|||
size = new Size(image.Width, image.Height); |
|||
} |
|||
|
|||
if (location == default(Point)) |
|||
{ |
|||
location = Point.Empty; |
|||
} |
|||
|
|||
source.ApplyProcessor(new DrawImageEffectProcessor<TPixel>(image, size, location, pixelFunc, percent), source.Bounds); |
|||
return source; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
// <copyright file="DrawImageEffectProcessor.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Drawing.Processors |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
using System.Threading.Tasks; |
|||
using ImageSharp.PixelFormats; |
|||
using ImageSharp.Processing; |
|||
|
|||
/// <summary>
|
|||
/// Combines two images together by blending the pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal class DrawImageEffectProcessor<TPixel> : ImageProcessor<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="DrawImageEffectProcessor{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="image">The image to blend with the currently processing image.</param>
|
|||
/// <param name="size">The size to draw the blended image.</param>
|
|||
/// <param name="location">The location to draw the blended image.</param>
|
|||
/// <param name="pixelFunction">Pixel function effect to apply on every pixel</param>
|
|||
/// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param>
|
|||
public DrawImageEffectProcessor(Image<TPixel> image, Size size, Point location, Func<TPixel, TPixel, float, TPixel> 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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the image to blend.
|
|||
/// </summary>
|
|||
public Image<TPixel> Image { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets The function effect to apply on a per pixel basis
|
|||
/// </summary>
|
|||
public Func<TPixel, TPixel, float, TPixel> PixelFunction { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the alpha percentage value.
|
|||
/// </summary>
|
|||
public int Alpha { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the size to draw the blended image.
|
|||
/// </summary>
|
|||
public Size Size { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the location to draw the blended image.
|
|||
/// </summary>
|
|||
public Point Location { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase<TPixel> 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<TPixel> sourcePixels = this.Image.Lock()) |
|||
using (PixelAccessor<TPixel> 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); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
// <copyright file="PixelTransformMode.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.PixelFormats |
|||
{ |
|||
/// <summary>
|
|||
/// Porter Duff Blending composition modes
|
|||
/// </summary>
|
|||
public enum PixelTransformMode |
|||
{ |
|||
/// <summary>
|
|||
/// Default blending mode, also known as "Normal" or "Alpha Blending"
|
|||
/// </summary>
|
|||
Normal, |
|||
|
|||
/// <summary>
|
|||
/// Backdrop + Source
|
|||
/// </summary>
|
|||
Multiply, |
|||
|
|||
/// <summary>
|
|||
/// Backdrop + Source
|
|||
/// </summary>
|
|||
Add, |
|||
|
|||
/// <summary>
|
|||
/// Backdrop - Source
|
|||
/// </summary>
|
|||
Substract, |
|||
|
|||
/// <summary>
|
|||
/// Screen effect
|
|||
/// </summary>
|
|||
Screen, |
|||
|
|||
/// <summary>
|
|||
/// Darken effect
|
|||
/// </summary>
|
|||
Darken, |
|||
|
|||
/// <summary>
|
|||
/// Lighten effect
|
|||
/// </summary>
|
|||
Lighten, |
|||
|
|||
/// <summary>
|
|||
/// Overlay effect
|
|||
/// </summary>
|
|||
Overlay, |
|||
|
|||
/// <summary>
|
|||
/// Hard light effect
|
|||
/// </summary>
|
|||
HardLight |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// <copyright file="PixelTransformModeExtensions.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using PixelFormats; |
|||
|
|||
/// <summary>
|
|||
/// Extensions to retrieve the appropiate pixel transformation functions for <see cref="PixelTransformMode"/>
|
|||
/// </summary>
|
|||
public static class PixelTransformModeExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a pixel transformation function
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format used for both Backdrop and Source</typeparam>
|
|||
/// <param name="mode">The Duff Porter mode</param>
|
|||
/// <returns>A function that transforms a Backdrop and Source colors into a final color</returns>
|
|||
public static Func<TPixel, TPixel, float, TPixel> GetPixelFunction<TPixel>(this PixelTransformMode mode) |
|||
where TPixel : IPixel |
|||
{ |
|||
return mode.GetPixelFunction<TPixel, TPixel>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a pixel transformation function
|
|||
/// </summary>
|
|||
/// <typeparam name="TBckPixel">The pixel format used for Backdrop and Output</typeparam>
|
|||
/// <typeparam name="TSrcPixel">The pixel format used for Source</typeparam>
|
|||
/// <param name="mode">The Duff Porter mode</param>
|
|||
/// <returns>A function that transforms a Backdrop and Source colors into a final color</returns>
|
|||
public static Func<TBckPixel, TSrcPixel, float, TBckPixel> GetPixelFunction<TBckPixel, TSrcPixel>(this PixelTransformMode mode) |
|||
where TBckPixel : IPixel |
|||
where TSrcPixel : IPixel |
|||
{ |
|||
switch (mode) |
|||
{ |
|||
case PixelTransformMode.Normal: return PorterDuffFunctions<TBckPixel, TSrcPixel>.NormalBlendFunction; |
|||
case PixelTransformMode.Multiply: return PorterDuffFunctions<TBckPixel, TSrcPixel>.MultiplyFunction; |
|||
case PixelTransformMode.Add: return PorterDuffFunctions<TBckPixel, TSrcPixel>.AddFunction; |
|||
case PixelTransformMode.Substract: return PorterDuffFunctions<TBckPixel, TSrcPixel>.SubstractFunction; |
|||
case PixelTransformMode.Screen: return PorterDuffFunctions<TBckPixel, TSrcPixel>.ScreenFunction; |
|||
case PixelTransformMode.Darken: return PorterDuffFunctions<TBckPixel, TSrcPixel>.DarkenFunction; |
|||
case PixelTransformMode.Lighten: return PorterDuffFunctions<TBckPixel, TSrcPixel>.LightenFunction; |
|||
case PixelTransformMode.Overlay: return PorterDuffFunctions<TBckPixel, TSrcPixel>.OverlayFunction; |
|||
case PixelTransformMode.HardLight: return PorterDuffFunctions<TBckPixel, TSrcPixel>.HardLightFunction; |
|||
|
|||
default: throw new NotImplementedException(nameof(mode)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,267 @@ |
|||
// <copyright file="PorterDuffFunctions.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.PixelFormats |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Collection of Porter Duff alpha blending functions
|
|||
/// </summary>
|
|||
/// <typeparam name="TBckPixel">Backdrop Pixel Format</typeparam>
|
|||
/// <typeparam name="TsrcPixel">Source Pixel Format</typeparam>
|
|||
/// <remarks>
|
|||
/// 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
|
|||
/// </remarks>
|
|||
internal static class PorterDuffFunctions<TBckPixel, TsrcPixel> |
|||
where TBckPixel : IPixel |
|||
where TsrcPixel : IPixel |
|||
{ |
|||
/// <summary>
|
|||
/// Source over backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Source multiplied by backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Source added to backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Source substracted from backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Complement of source multiplied by the complement of backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Per element, chooses the smallest value of source and backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Per element, chooses the largest value of source and backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Overlays source over backdrop
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Hard light effect
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backgrop color</param>
|
|||
/// <param name="source">Source color</param>
|
|||
/// <param name="opacity">Opacity applied to Source Alpha</param>
|
|||
/// <returns>Output color</returns>
|
|||
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))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper function for Overlay andHardLight modes
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Backdrop color element</param>
|
|||
/// <param name="source">Source color element</param>
|
|||
/// <returns>Overlay value</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static float OverlayValueFunction(float backdrop, float source) |
|||
{ |
|||
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// General composition function for all modes, with a general solution for alpha channel
|
|||
/// </summary>
|
|||
/// <param name="backdrop">Original backgrop color</param>
|
|||
/// <param name="source">Original source color</param>
|
|||
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
|
|||
/// <returns>The final color</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) |
|||
{ |
|||
DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); |
|||
|
|||
// 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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// <copyright file="DrawImageEffectTest.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue