Browse Source

migrate blenders to PorterDuff

pull/202/head
Scott Williams 9 years ago
parent
commit
ad1bb5dbee
  1. 4
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  2. 100
      src/ImageSharp.Drawing/DrawImage.cs
  3. 6
      src/ImageSharp.Drawing/GraphicsOptions.cs
  4. 90
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  5. 4
      src/ImageSharp/ImageProcessor.cs
  6. 51
      src/ImageSharp/PixelFormats/PixelBlenderMode.cs
  7. 17
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs
  8. 13
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
  9. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs
  10. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs
  11. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs
  12. 13
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
  13. 15
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
  14. 13
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
  15. 10
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
  16. 13
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
  17. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs
  18. 13
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
  19. 17
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs
  20. 85
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  21. 58
      src/ImageSharp/PixelFormats/PixelTransformMode.cs
  22. 57
      src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs
  23. 39
      src/ImageSharp/PixelFormats/PorterDuffFunctions.cs
  24. 7
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  25. 2
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs
  26. 10
      tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs
  27. 2
      tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
  28. 18
      tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs

4
src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs

@ -155,10 +155,10 @@ namespace ImageSharp.Drawing.Brushes
{ {
for (int i = 0; i < scanline.Length; i++) for (int i = 0; i < scanline.Length; i++)
{ {
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
int patternX = (x + i) % this.pattern.Width; int patternX = (x + i) % this.pattern.Width;
overlay[i] = this.pattern[y, x]; overlay[i] = this.pattern[patternY, patternX];
} }
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);

100
src/ImageSharp.Drawing/DrawImage.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{ {
using System; using System;
using Drawing.Processors; using Drawing.Processors;
using ImageSharp.Drawing;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
@ -14,31 +15,17 @@ namespace ImageSharp
/// </summary> /// </summary>
public static partial class ImageExtensions 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> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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) public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, Size size, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (size == default(Size)) if (size == default(Size))
@ -51,27 +38,56 @@ namespace ImageSharp
location = Point.Empty; location = Point.Empty;
} }
source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, percent), source.Bounds); source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, size, location, options), source.Bounds);
return source; return source;
} }
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</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> /// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent)
where TPixel : struct, IPixel<TPixel>
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlendPercentage = percent;
return DrawImage(source, image, default(Size), default(Point), options);
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <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="source">The image this method extends.</param>
/// <param name="size">The size to draw the blended image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="blender">The blending mode.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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) public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float percent)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Func<TPixel, TPixel, float, TPixel> pixelFunc = mode.GetPixelFunction<TPixel>(); GraphicsOptions options = GraphicsOptions.Default;
options.BlendPercentage = percent;
options.BlenderMode = blender;
return DrawImage(source, image, default(Size), default(Point), options);
}
return DrawImage(source, image, pixelFunc, percent, size, location); /// <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="options">The options, including the blending type and belnding amount.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return DrawImage(source, image, default(Size), default(Point), options);
} }
/// <summary> /// <summary>
@ -79,27 +95,37 @@ namespace ImageSharp
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</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> /// <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="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="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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) public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent, Size size, Point location)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (size == default(Size)) GraphicsOptions options = GraphicsOptions.Default;
{ options.BlendPercentage = percent;
size = new Size(image.Width, image.Height); return source.DrawImage(image, size, location, options);
} }
if (location == default(Point))
{
location = Point.Empty;
}
source.ApplyProcessor(new DrawImageEffectProcessor<TPixel>(image, size, location, pixelFunc, percent), source.Bounds); /// <summary>
return source; /// 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="blender">The type of bending to apply.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 1.</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, PixelBlenderMode blender, float percent, Size size, Point location)
where TPixel : struct, IPixel<TPixel>
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlenderMode = blender;
options.BlendPercentage = percent;
return source.DrawImage(image, size, location, options);
} }
} }
} }

6
src/ImageSharp.Drawing/GraphicsOptions.cs

@ -31,7 +31,7 @@ namespace ImageSharp.Drawing
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> /// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing) public GraphicsOptions(bool enableAntialiasing)
{ {
this.blenderMode = PixelBlenderMode.Default; this.blenderMode = PixelBlenderMode.Normal;
this.blendPercentage = 1; this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16; this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
@ -64,6 +64,10 @@ namespace ImageSharp.Drawing
set => this.blendPercentage = value; set => this.blendPercentage = value;
} }
// In the future we could expose a PixelBlender<TPixel> directly on here
// or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1.
/// <summary> /// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary> /// </summary>

90
src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs

@ -18,19 +18,22 @@ namespace ImageSharp.Drawing.Processors
internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel> internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly PixelBlender<TPixel> blender;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="image">The image to blend with the currently processing image.</param> /// <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="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param> /// <param name="options">The opacity of the image to blend. Between 0 and 100.</param>
public DrawImageProcessor(Image<TPixel> image, Size size, Point location, int alpha = 100) public DrawImageProcessor(Image<TPixel> image, Size size, Point location, GraphicsOptions options)
{ {
Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage));
this.Image = image; this.Image = image;
this.Size = size; this.Size = size;
this.Alpha = alpha; this.Alpha = options.BlendPercentage;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.Location = location; this.Location = location;
} }
@ -42,7 +45,7 @@ namespace ImageSharp.Drawing.Processors
/// <summary> /// <summary>
/// Gets the alpha percentage value. /// Gets the alpha percentage value.
/// </summary> /// </summary>
public int Alpha { get; } public float Alpha { get; }
/// <summary> /// <summary>
/// Gets the size to draw the blended image. /// Gets the size to draw the blended image.
@ -57,43 +60,60 @@ namespace ImageSharp.Drawing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{ {
if (this.Image.Bounds.Size != this.Size) Image<TPixel> disposableImage = null;
Image<TPixel> targetImage = this.Image;
try
{ {
// should Resize be moved to core? if (targetImage.Bounds.Size != this.Size)
this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); {
} // should Resize be moved to core?
targetImage = disposableImage = new Image<TPixel>(this.Image).Resize(this.Size.Width, this.Size.Height);
}
// Align start/end positions. // Align start/end positions.
Rectangle bounds = this.Image.Bounds; Rectangle bounds = this.Image.Bounds;
int minX = Math.Max(this.Location.X, sourceRectangle.X); int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
float alpha = this.Alpha / 100F; int width = maxX - minX;
using (Buffer<float> amount = new Buffer<float>(width))
using (PixelAccessor<TPixel> toBlendPixels = this.Image.Lock())
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
for (int i = 0; i < width; i++)
{
amount[i] = this.Alpha;
}
using (PixelAccessor<TPixel> toBlendPixels = this.Image.Lock()) Parallel.For(
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) minY,
{ maxY,
Parallel.For( this.ParallelOptions,
minY, y =>
maxY,
this.ParallelOptions,
y =>
{
for (int x = minX; x < maxX; x++)
{ {
Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); BufferSpan<TPixel> background = sourcePixels.GetRowSpan(y).Slice(minX, width);
Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4(); BufferSpan<TPixel> foreground = toBlendPixels.GetRowSpan(y - minY).Slice(0, width);
this.blender.Compose(background, background, foreground, amount);
// Lerping colors is dependent on the alpha of the blended color // for (int x = minX; x < maxX; x++)
backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha); // {
// Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
TPixel packed = default(TPixel); // Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4();
packed.PackFromVector4(backgroundVector); // // Lerping colors is dependent on the alpha of the blended color
sourcePixels[x, y] = packed; // backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha);
} // TPixel packed = default(TPixel);
// packed.PackFromVector4(backgroundVector);
// sourcePixels[x, y] = packed;
// }
}); });
}
}
finally
{
disposableImage?.Dispose();
} }
} }
} }

4
src/ImageSharp/ImageProcessor.cs

@ -41,7 +41,11 @@ namespace ImageSharp.Processing
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
throw;
#else
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
#endif
} }
} }

51
src/ImageSharp/PixelFormats/PixelBlenderMode.cs

@ -15,69 +15,48 @@ namespace ImageSharp.PixelFormats
public enum PixelBlenderMode public enum PixelBlenderMode
{ {
/// <summary> /// <summary>
/// The default composition mode. /// Default blending mode, also known as "Normal" or "Alpha Blending"
/// </summary> /// </summary>
/// <remarks> uses PremultipliedLerpTransform </remarks> Normal = 0,
Default = 0,
/// <summary> /// <summary>
/// Normal transform. /// Backdrop + Source
/// </summary>
Normal,
/// <summary>
/// Multiply Transform.
/// </summary> /// </summary>
Multiply, Multiply,
/// <summary> /// <summary>
/// Screen Transform. /// Backdrop + Source
/// </summary> /// </summary>
Screen, Add,
/// <summary> /// <summary>
/// HardLight Transform. /// Backdrop - Source
/// </summary> /// </summary>
HardLight, Substract,
/// <summary> /// <summary>
/// Overlay Transform. /// Screen effect
/// </summary> /// </summary>
Overlay, Screen,
/// <summary> /// <summary>
/// Darken Transform. /// Darken effect
/// </summary> /// </summary>
Darken, Darken,
/// <summary> /// <summary>
/// Lighten Transform. /// Lighten effect
/// </summary> /// </summary>
Lighten, Lighten,
/// <summary> /// <summary>
/// SoftLight Transform. /// Overlay effect
/// </summary>
SoftLight,
/// <summary>
/// Dodge Transform.
/// </summary>
Dodge,
/// <summary>
/// Burn Transform.
/// </summary> /// </summary>
Burn, Overlay,
/// <summary>
/// Difference Transform.
/// </summary>
Difference,
/// <summary> /// <summary>
/// Exclusion Transform. /// Hard light effect
/// </summary> /// </summary>
Exclusion HardLight
} }
} }

17
src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs → src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs

@ -1,4 +1,4 @@
// <copyright file="DefaultBurnPixelBlender{TPixel}.cs" company="James Jackson-South"> // <copyright file="DefaultAddPixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -13,16 +13,18 @@ namespace ImageSharp.PixelFormats.PixelBlenders
/// Abstract base class for calling pixel composition functions /// Abstract base class for calling pixel composition functions
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultBurnPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultAddPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultAddPixelBlender<TPixel> Instance { get; } = new DefaultAddPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.Burn(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.AddFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.Burn(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.AddFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

13
src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultDarkenPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultDarkenPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultDarkenPixelBlender<TPixel> Instance { get; } = new DefaultDarkenPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.Darken(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.DarkenFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.Darken(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.DarkenFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

42
src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs

@ -1,42 +0,0 @@
// <copyright file="DefaultDifferencePixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats.PixelBlenders
{
using System;
using System.Numerics;
using ImageSharp.PixelFormats;
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultDifferencePixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Difference(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
}
/// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{
Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Difference(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

42
src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs

@ -1,42 +0,0 @@
// <copyright file="DefaultDodgePixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats.PixelBlenders
{
using System;
using System.Numerics;
using ImageSharp.PixelFormats;
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultDodgePixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Dodge(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
}
/// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{
Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Dodge(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

42
src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs

@ -1,42 +0,0 @@
// <copyright file="DefaultExclusionPixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats.PixelBlenders
{
using System;
using System.Numerics;
using ImageSharp.PixelFormats;
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultExclusionPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Exclusion(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
}
/// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{
Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Exclusion(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

13
src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultHardLightPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultHardLightPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultHardLightPixelBlender<TPixel> Instance { get; } = new DefaultHardLightPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.HardLight(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.HardLightFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.HardLight(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.HardLightFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

15
src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs

@ -1,4 +1,4 @@
// <copyright file="DefaultDarkenPixelBlender{TPixel}.cs" company="James Jackson-South"> // <copyright file="DefaultLightenPixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultLightenPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultLightenPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultLightenPixelBlender<TPixel> Instance { get; } = new DefaultLightenPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.Lighten(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.LightenFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.Lighten(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.LightenFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

13
src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultMultiplyPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultMultiplyPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultMultiplyPixelBlender<TPixel> Instance { get; } = new DefaultMultiplyPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.Multiply(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.MultiplyFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.Multiply(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.MultiplyFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

10
src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs

@ -6,6 +6,7 @@
namespace ImageSharp.PixelFormats.PixelBlenders namespace ImageSharp.PixelFormats.PixelBlenders
{ {
using System; using System;
using System.Numerics;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
@ -15,10 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultNormalPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultNormalPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultNormalPixelBlender<TPixel> Instance { get; } = new DefaultNormalPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
return source; return PorterDuffFunctions<TPixel>.NormalBlendFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -30,7 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destination[i] = source[i]; destination[i] = PorterDuffFunctions<TPixel>.NormalBlendFunction(destination[i], source[i], amount[i]);
} }
} }
} }

13
src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultOverlayPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultOverlayPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultOverlayPixelBlender<TPixel> Instance { get; } = new DefaultOverlayPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.Overlay(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.OverlayFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.Overlay(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.OverlayFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

42
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs

@ -1,42 +0,0 @@
// <copyright file="DefaultDifferencePixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.PixelFormats.PixelBlenders
{
using System;
using System.Numerics;
using ImageSharp.PixelFormats;
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultPremultipliedLerpPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background.ToVector4(), source.ToVector4(), amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
}
/// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{
Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination));
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background[i].ToVector4(), source[i].ToVector4(), amount[i]);
destination[i].PackFromVector4(result);
}
}
}
}

13
src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultScreenPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultScreenPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultScreenPixelBlender<TPixel> Instance { get; } = new DefaultScreenPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.Screen(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.ScreenFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.Screen(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.ScreenFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

17
src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs → src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs

@ -1,4 +1,4 @@
// <copyright file="DefaultSoftLightPixelBlender{TPixel}.cs" company="James Jackson-South"> // <copyright file="DefaultSubstractPixelBlender{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -13,16 +13,18 @@ namespace ImageSharp.PixelFormats.PixelBlenders
/// Abstract base class for calling pixel composition functions /// Abstract base class for calling pixel composition functions
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultSoftLightPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultSubstractPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultSubstractPixelBlender<TPixel> Instance { get; } = new DefaultSubstractPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Compose(TPixel background, TPixel source, float amount)
{ {
Vector4 result = Vector4BlendTransforms.SoftLight(background.ToVector4(), source.ToVector4()); return PorterDuffFunctions<TPixel>.SubstractFunction(background, source, amount);
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
Vector4 result = Vector4BlendTransforms.SoftLight(background[i].ToVector4(), source[i].ToVector4()); destination[i] = PorterDuffFunctions<TPixel>.SubstractFunction(destination[i], source[i], amount[i]);
destination[i].PackFromVector4(result);
} }
} }
} }

85
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs

@ -34,56 +34,6 @@ namespace ImageSharp.PixelFormats
/// </summary> /// </summary>
private PixelBlender<TPixel> screenBlender = new DefaultScreenPixelBlender<TPixel>(); private PixelBlender<TPixel> screenBlender = new DefaultScreenPixelBlender<TPixel>();
/// <summary>
/// Gets the HardLightBlender.
/// </summary>
private PixelBlender<TPixel> hardLightBlender = new DefaultHardLightPixelBlender<TPixel>();
/// <summary>
/// Gets the OverlayBlender.
/// </summary>
private PixelBlender<TPixel> overlayBlender = new DefaultOverlayPixelBlender<TPixel>();
/// <summary>
/// Gets the DarkenBlender.
/// </summary>
private PixelBlender<TPixel> darkenBlender = new DefaultDarkenPixelBlender<TPixel>();
/// <summary>
/// Gets the LightenBlender.
/// </summary>
private PixelBlender<TPixel> lightenBlender = new DefaultLightenPixelBlender<TPixel>();
/// <summary>
/// Gets the SoftLightBlender.
/// </summary>
private PixelBlender<TPixel> softLightBlender = new DefaultSoftLightPixelBlender<TPixel>();
/// <summary>
/// Gets the DodgeBlender.
/// </summary>
private PixelBlender<TPixel> dodgeBlender = new DefaultDodgePixelBlender<TPixel>();
/// <summary>
/// Gets the BurnBlender.
/// </summary>
private PixelBlender<TPixel> burnBlender = new DefaultBurnPixelBlender<TPixel>();
/// <summary>
/// Gets the DifferenceBlender.
/// </summary>
private PixelBlender<TPixel> differenceBlender = new DefaultDifferencePixelBlender<TPixel>();
/// <summary>
/// Gets the DifferenceBlender.
/// </summary>
private PixelBlender<TPixel> exclusionBlender = new DefaultExclusionPixelBlender<TPixel>();
/// <summary>
/// Gets the PremultipliedLerpBlender.
/// </summary>
private PixelBlender<TPixel> premultipliedLerpBlender = new DefaultPremultipliedLerpPixelBlender<TPixel>();
/// <summary> /// <summary>
/// Find an instance of the pixel blender. /// Find an instance of the pixel blender.
/// </summary> /// </summary>
@ -93,32 +43,25 @@ namespace ImageSharp.PixelFormats
{ {
switch (mode) switch (mode)
{ {
case PixelBlenderMode.Normal:
return this.normalBlender;
case PixelBlenderMode.Multiply: case PixelBlenderMode.Multiply:
return this.multiplyBlender; return DefaultMultiplyPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Add:
return DefaultAddPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Substract:
return DefaultSubstractPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Screen: case PixelBlenderMode.Screen:
return this.screenBlender; return DefaultScreenPixelBlender<TPixel>.Instance;
case PixelBlenderMode.HardLight:
return this.hardLightBlender;
case PixelBlenderMode.Overlay:
return this.overlayBlender;
case PixelBlenderMode.Darken: case PixelBlenderMode.Darken:
return this.darkenBlender; return DefaultDarkenPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Lighten: case PixelBlenderMode.Lighten:
return this.lightenBlender; return DefaultLightenPixelBlender<TPixel>.Instance;
case PixelBlenderMode.SoftLight: case PixelBlenderMode.Overlay:
return this.softLightBlender; return DefaultOverlayPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Dodge: case PixelBlenderMode.HardLight:
return this.dodgeBlender; return DefaultHardLightPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Burn: case PixelBlenderMode.Normal:
return this.burnBlender;
case PixelBlenderMode.Difference:
return this.differenceBlender;
case PixelBlenderMode.Exclusion:
return this.exclusionBlender;
default: default:
return this.premultipliedLerpBlender; return DefaultNormalPixelBlender<TPixel>.Instance;
} }
} }
} }

58
src/ImageSharp/PixelFormats/PixelTransformMode.cs

@ -1,58 +0,0 @@
// <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
}
}

57
src/ImageSharp/PixelFormats/PixelTransformModeExtensions.cs

@ -1,57 +0,0 @@
// <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));
}
}
}
}

39
src/ImageSharp/PixelFormats/PorterDuffFunctions.cs

@ -11,8 +11,7 @@ namespace ImageSharp.PixelFormats
/// <summary> /// <summary>
/// Collection of Porter Duff alpha blending functions /// Collection of Porter Duff alpha blending functions
/// </summary> /// </summary>
/// <typeparam name="TBckPixel">Backdrop Pixel Format</typeparam> /// <typeparam name="TPixel">Pixel Format</typeparam>
/// <typeparam name="TsrcPixel">Source Pixel Format</typeparam>
/// <remarks> /// <remarks>
/// These functions are designed to be a general solution for all color cases, /// 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 /// that is, they take in account the alpha value of both the backdrop
@ -21,9 +20,8 @@ namespace ImageSharp.PixelFormats
/// Note there are faster functions for when the backdrop color is known /// Note there are faster functions for when the backdrop color is known
/// to be opaque /// to be opaque
/// </remarks> /// </remarks>
internal static class PorterDuffFunctions<TBckPixel, TsrcPixel> internal static class PorterDuffFunctions<TPixel>
where TBckPixel : IPixel where TPixel : IPixel
where TsrcPixel : IPixel
{ {
/// <summary> /// <summary>
/// Source over backdrop /// Source over backdrop
@ -32,7 +30,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel NormalBlendFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -53,7 +52,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel MultiplyFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -74,7 +74,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel AddFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -95,7 +96,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel SubstractFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -116,7 +118,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel ScreenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -137,7 +140,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel DarkenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -158,7 +162,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel LightenFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -179,7 +184,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel OverlayFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -204,7 +210,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param> /// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
public static TBckPixel HardLightFunction(TBckPixel backdrop, TsrcPixel source, float opacity) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity)
{ {
Vector4 l = source.ToVector4(); Vector4 l = source.ToVector4();
l.W *= opacity; l.W *= opacity;
@ -242,7 +249,7 @@ namespace ImageSharp.PixelFormats
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param> /// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
/// <returns>The final color</returns> /// <returns>The final color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform) private static TPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform)
{ {
DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W));
@ -258,7 +265,7 @@ namespace ImageSharp.PixelFormats
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a;
xform.W = a; xform.W = a;
TBckPixel packed = default(TBckPixel); TPixel packed = default(TPixel);
packed.PackFromVector4(xform); packed.PackFromVector4(xform);
return packed; return packed;

7
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs

@ -76,18 +76,19 @@ namespace ImageSharp.Processing.Processors
} }
// TODO move GraphicOptions into core so all processes can use it. // TODO move GraphicOptions into core so all processes can use it.
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Default); PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Normal);
for (int y = minY; y < maxY; y++) for (int y = minY; y < maxY; y++)
{ {
int offsetY = y - startY; int offsetY = y - startY;
int offsetX = minX - startX; int offsetX = minX - startX;
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
{ {
float distance = Vector2.Distance(centre, new Vector2((i + offsetX), offsetY)); float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
amounts[i] = 1 - (.95F * (distance / maxDistance)); amounts[i] = (1 - (.95F * (distance / maxDistance))).Clamp(0, 1);
} }
BufferSpan<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); BufferSpan<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width);
blender.Compose(destination, destination, rowColors, amounts); blender.Compose(destination, destination, rowColors, amounts);
} }
} }

2
src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs

@ -74,8 +74,6 @@ namespace ImageSharp.Processing.Processors
rowColors[i] = glowColor; rowColors[i] = glowColor;
} }
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Default);
Parallel.For( Parallel.For(
minY, minY,
maxY, maxY,

10
tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Tests namespace ImageSharp.Tests
{ {
using System.IO; using System.IO;
using ImageSharp.Drawing;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using Xunit; using Xunit;
@ -16,7 +17,7 @@ namespace ImageSharp.Tests
{ {
string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect"); string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect");
PixelTransformMode[] modes = (PixelTransformMode[])System.Enum.GetValues(typeof(PixelTransformMode)); PixelBlenderMode[] modes = (PixelBlenderMode[])System.Enum.GetValues(typeof(PixelBlenderMode));
using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage()) using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage())
{ {
@ -24,14 +25,17 @@ namespace ImageSharp.Tests
{ {
using (Image image = file.CreateImage()) using (Image image = file.CreateImage())
{ {
foreach (PixelTransformMode mode in modes) foreach (PixelBlenderMode mode in modes)
{ {
using (FileStream output = File.OpenWrite($"{path}/{mode}.{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{mode}.{file.FileName}"))
{ {
Size size = new Size(image.Width / 2, image.Height / 2); Size size = new Size(image.Width / 2, image.Height / 2);
Point loc = new Point(image.Width / 4, image.Height / 4); Point loc = new Point(image.Width / 4, image.Height / 4);
image.DrawImage(blend, mode, 75, size, loc).Save(output); image.DrawImage(blend, size, loc, new GraphicsOptions() {
BlenderMode = mode,
BlendPercentage = .75f
}).Save(output);
} }
} }
} }

2
tests/ImageSharp.Tests/Drawing/DrawImageTest.cs

@ -24,7 +24,7 @@ namespace ImageSharp.Tests
{ {
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{ {
image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) image.DrawImage(blend, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))
.Save(output); .Save(output);
} }
} }

18
tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs

@ -14,31 +14,25 @@ namespace ImageSharp.Tests.PixelFormats
{ {
public static TheoryData<object, Type, PixelBlenderMode> blenderMappings = new TheoryData<object, Type, PixelBlenderMode>() public static TheoryData<object, Type, PixelBlenderMode> blenderMappings = new TheoryData<object, Type, PixelBlenderMode>()
{ {
{ new TestPixel<Rgba32>(), typeof(DefaultPremultipliedLerpPixelBlender<Rgba32>), PixelBlenderMode.Default },
{ new TestPixel<Rgba32>(), typeof(DefaultNormalPixelBlender<Rgba32>), PixelBlenderMode.Normal }, { new TestPixel<Rgba32>(), typeof(DefaultNormalPixelBlender<Rgba32>), PixelBlenderMode.Normal },
{ new TestPixel<Rgba32>(), typeof(DefaultScreenPixelBlender<Rgba32>), PixelBlenderMode.Screen }, { new TestPixel<Rgba32>(), typeof(DefaultScreenPixelBlender<Rgba32>), PixelBlenderMode.Screen },
{ new TestPixel<Rgba32>(), typeof(DefaultHardLightPixelBlender<Rgba32>), PixelBlenderMode.HardLight }, { new TestPixel<Rgba32>(), typeof(DefaultHardLightPixelBlender<Rgba32>), PixelBlenderMode.HardLight },
{ new TestPixel<Rgba32>(), typeof(DefaultOverlayPixelBlender<Rgba32>), PixelBlenderMode.Overlay }, { new TestPixel<Rgba32>(), typeof(DefaultOverlayPixelBlender<Rgba32>), PixelBlenderMode.Overlay },
{ new TestPixel<Rgba32>(), typeof(DefaultDarkenPixelBlender<Rgba32>), PixelBlenderMode.Darken }, { new TestPixel<Rgba32>(), typeof(DefaultDarkenPixelBlender<Rgba32>), PixelBlenderMode.Darken },
{ new TestPixel<Rgba32>(), typeof(DefaultLightenPixelBlender<Rgba32>), PixelBlenderMode.Lighten }, { new TestPixel<Rgba32>(), typeof(DefaultLightenPixelBlender<Rgba32>), PixelBlenderMode.Lighten },
{ new TestPixel<Rgba32>(), typeof(DefaultSoftLightPixelBlender<Rgba32>), PixelBlenderMode.SoftLight }, { new TestPixel<Rgba32>(), typeof(DefaultAddPixelBlender<Rgba32>), PixelBlenderMode.Add },
{ new TestPixel<Rgba32>(), typeof(DefaultDodgePixelBlender<Rgba32>), PixelBlenderMode.Dodge }, { new TestPixel<Rgba32>(), typeof(DefaultSubstractPixelBlender<Rgba32>), PixelBlenderMode.Substract },
{ new TestPixel<Rgba32>(), typeof(DefaultBurnPixelBlender<Rgba32>), PixelBlenderMode.Burn }, { new TestPixel<Rgba32>(), typeof(DefaultMultiplyPixelBlender<Rgba32>), PixelBlenderMode.Multiply },
{ new TestPixel<Rgba32>(), typeof(DefaultDifferencePixelBlender<Rgba32>), PixelBlenderMode.Difference },
{ new TestPixel<Rgba32>(), typeof(DefaultExclusionPixelBlender<Rgba32>), PixelBlenderMode.Exclusion },
{ new TestPixel<RgbaVector>(), typeof(DefaultPremultipliedLerpPixelBlender<RgbaVector>), PixelBlenderMode.Default },
{ new TestPixel<RgbaVector>(), typeof(DefaultNormalPixelBlender<RgbaVector>), PixelBlenderMode.Normal }, { new TestPixel<RgbaVector>(), typeof(DefaultNormalPixelBlender<RgbaVector>), PixelBlenderMode.Normal },
{ new TestPixel<RgbaVector>(), typeof(DefaultScreenPixelBlender<RgbaVector>), PixelBlenderMode.Screen }, { new TestPixel<RgbaVector>(), typeof(DefaultScreenPixelBlender<RgbaVector>), PixelBlenderMode.Screen },
{ new TestPixel<RgbaVector>(), typeof(DefaultHardLightPixelBlender<RgbaVector>), PixelBlenderMode.HardLight }, { new TestPixel<RgbaVector>(), typeof(DefaultHardLightPixelBlender<RgbaVector>), PixelBlenderMode.HardLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultOverlayPixelBlender<RgbaVector>), PixelBlenderMode.Overlay }, { new TestPixel<RgbaVector>(), typeof(DefaultOverlayPixelBlender<RgbaVector>), PixelBlenderMode.Overlay },
{ new TestPixel<RgbaVector>(), typeof(DefaultDarkenPixelBlender<RgbaVector>), PixelBlenderMode.Darken }, { new TestPixel<RgbaVector>(), typeof(DefaultDarkenPixelBlender<RgbaVector>), PixelBlenderMode.Darken },
{ new TestPixel<RgbaVector>(), typeof(DefaultLightenPixelBlender<RgbaVector>), PixelBlenderMode.Lighten }, { new TestPixel<RgbaVector>(), typeof(DefaultLightenPixelBlender<RgbaVector>), PixelBlenderMode.Lighten },
{ new TestPixel<RgbaVector>(), typeof(DefaultSoftLightPixelBlender<RgbaVector>), PixelBlenderMode.SoftLight }, { new TestPixel<RgbaVector>(), typeof(DefaultAddPixelBlender<RgbaVector>), PixelBlenderMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultDodgePixelBlender<RgbaVector>), PixelBlenderMode.Dodge }, { new TestPixel<RgbaVector>(), typeof(DefaultSubstractPixelBlender<RgbaVector>), PixelBlenderMode.Substract },
{ new TestPixel<RgbaVector>(), typeof(DefaultBurnPixelBlender<RgbaVector>), PixelBlenderMode.Burn }, { new TestPixel<RgbaVector>(), typeof(DefaultMultiplyPixelBlender<RgbaVector>), PixelBlenderMode.Multiply },
{ new TestPixel<RgbaVector>(), typeof(DefaultDifferencePixelBlender<RgbaVector>), PixelBlenderMode.Difference },
{ new TestPixel<RgbaVector>(), typeof(DefaultExclusionPixelBlender<RgbaVector>), PixelBlenderMode.Exclusion }
}; };
[Theory] [Theory]

Loading…
Cancel
Save