Browse Source

Merge pull request #679 from vpenades/master

Split PixelBlendMode into PixelColorBlendingMode and PixelAlphaCompositionMode
af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
b30a46bdbe
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  2. 85
      src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs
  3. 70
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  4. 16
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  5. 22
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  6. 113
      src/ImageSharp/GraphicsOptions.cs
  7. 63
      src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs
  8. 56
      src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs
  9. 265
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  10. 2
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs
  11. 2
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  12. 4
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  13. 32
      tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
  14. 68
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs
  15. 65
      tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs
  16. 101
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs
  17. 74
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs
  18. 11
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs
  19. 2
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  20. 2
      tests/Images/External

2
src/ImageSharp.Drawing/Processing/BrushApplicator.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing
{
this.Target = target;
this.Options = options;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
}
/// <summary>

85
src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, opacity));
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
@ -30,63 +30,92 @@ namespace SixLabors.ImageSharp.Processing
/// <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="blender">The blending mode.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float opacity)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, opacity, blender));
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <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="options">The options, including the blending type and blending amount.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, Image<TPixel> image)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, options));
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <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 blending amount.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</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="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float opacity, Point location)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, opacity));
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <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>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="blender">The type of bending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</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="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelBlenderMode blender, float opacity, Point location)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, opacity, blender));
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <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="options">The options containing the blend mode and opacity.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// </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="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, alphaComposition, opacity));
/// <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="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, Image<TPixel> image, Point location)
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, options));
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
}
}

70
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -18,80 +18,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <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="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixel> image, float opacity)
: this(image, Point.Empty, opacity)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="options">
/// The options containing the opacity of the image to blend and blending mode.
/// Opacity must be between 0 and 1.
/// </param>
public DrawImageProcessor(Image<TPixel> image, GraphicsOptions options)
: this(image, Point.Empty, options)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <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="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixel> image, Point location, float opacity)
: this(image, location, opacity, GraphicsOptions.Default.BlenderMode)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <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="options">
/// The options containing the opacity of the image to blend and blending mode.
/// Opacity must be between 0 and 1.
/// </param>
public DrawImageProcessor(Image<TPixel> image, Point location, GraphicsOptions options)
: this(image, location, options.BlendPercentage, options.BlenderMode)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <param name="blenderMode">The blending mode to use when drawing the image.</param>
public DrawImageProcessor(Image<TPixel> image, float opacity, PixelBlenderMode blenderMode)
: this(image, Point.Empty, opacity, blenderMode)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// </summary>
/// <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="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <param name="blenderMode">The blending mode to use when drawing the image.</param>
public DrawImageProcessor(Image<TPixel> image, Point location, float opacity, PixelBlenderMode blenderMode)
public DrawImageProcessor(Image<TPixel> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image;
this.Opacity = opacity;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(blenderMode);
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location;
}

16
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -102,14 +102,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
{
solidBrush = this.brush as SolidBrush<TPixel>;
return solidBrush != null
&& ((this.options.BlenderMode == PixelBlenderMode.Normal && this.options.BlendPercentage == 1f
&& solidBrush.Color.ToVector4().W == 1f)
|| (this.options.BlenderMode == PixelBlenderMode.Over && this.options.BlendPercentage == 1f
&& solidBrush.Color.ToVector4().W == 1f)
|| (this.options.BlenderMode == PixelBlenderMode.Src));
solidBrush = this.brush as SolidBrush<TPixel>;
if (solidBrush == null)
{
return false;
}
return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
}

22
src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs

@ -32,7 +32,9 @@ namespace SixLabors.ImageSharp.Processing
private float? dpiY;
private PixelBlenderMode blenderMode;
private PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode;
private float wrapTextWidth;
@ -53,7 +55,8 @@ namespace SixLabors.ImageSharp.Processing
this.verticalAlignment = VerticalAlignment.Top;
this.antialiasSubpixelDepth = 16;
this.blenderMode = PixelBlenderMode.Normal;
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1;
this.antialias = enableAntialiasing;
this.dpiX = DefaultTextDpi;
@ -80,9 +83,14 @@ namespace SixLabors.ImageSharp.Processing
// some API thought post V1.
/// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get => this.colorBlendingMode; set => this.colorBlendingMode = value; }
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelBlenderMode BlenderMode { get => this.blenderMode; set => this.blenderMode = value; }
public PixelAlphaCompositionMode AlphaCompositionMode { get => this.alphaCompositionMode; set => this.alphaCompositionMode = value; }
/// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
@ -135,7 +143,8 @@ namespace SixLabors.ImageSharp.Processing
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage,
blenderMode = options.BlenderMode
colorBlendingMode = options.ColorBlendingMode,
alphaCompositionMode = options.AlphaCompositionMode
};
}
@ -151,7 +160,8 @@ namespace SixLabors.ImageSharp.Processing
return new GraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
BlenderMode = options.BlenderMode,
ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode,
BlendPercentage = options.BlendPercentage
};
}

113
src/ImageSharp/GraphicsOptions.cs

@ -21,7 +21,9 @@ namespace SixLabors.ImageSharp
private bool? antialias;
private PixelBlenderMode blenderMode;
private PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
@ -29,10 +31,62 @@ namespace SixLabors.ImageSharp
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing)
{
this.blenderMode = PixelBlenderMode.Normal;
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
/// <param name="blending">color blending mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
/// <param name="blending">color blending mode to apply to the drawing operation</param>
/// <param name="composition">alpha composition mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = composition;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
@ -67,12 +121,59 @@ namespace SixLabors.ImageSharp
// some API thought post V1.
/// <summary>
/// Gets or sets a value indicating the blending mode to apply to the drawing operation
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
}
/// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// </summary>
public PixelBlenderMode BlenderMode
public PixelAlphaCompositionMode AlphaCompositionMode
{
get => this.blenderMode;
set => this.blenderMode = value;
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
}
/// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
/// <param name="color">the color</param>
/// <returns>true if the color can be considered opaque</returns>
/// <remarks>
/// Blending and composition is an expensive operation, in some cases, like
/// filling with a solid color, the blending can be avoided by a plain color replacement.
/// This method can be useful for such processors to select the fast path.
/// </remarks>
internal bool IsOpaqueColorWithoutBlending<TPixel>(TPixel color)
where TPixel : struct, IPixel<TPixel>
{
if (this.ColorBlendingMode != PixelColorBlendingMode.Normal)
{
return false;
}
if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver &&
this.AlphaCompositionMode != PixelAlphaCompositionMode.Src)
{
return false;
}
if (this.BlendPercentage != 1f)
{
return false;
}
if (color.ToVector4().W != 1f)
{
return false;
}
return true;
}
}
}

63
src/ImageSharp/PixelFormats/PixelBlenderMode.cs → src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs

@ -4,55 +4,15 @@
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Enumerates the various blending modes.
/// Enumerates the various alpha composition modes.
/// </summary>
public enum PixelBlenderMode
{
public enum PixelAlphaCompositionMode
{
/// <summary>
/// Default blending mode, also known as "Normal" or "Alpha Blending"
/// </summary>
Normal = 0,
/// <summary>
/// Blends the 2 values by multiplication.
/// </summary>
Multiply,
/// <summary>
/// Blends the 2 values by addition.
/// </summary>
Add,
/// <summary>
/// Blends the 2 values by subtraction.
/// </summary>
Subtract,
/// <summary>
/// Multiplies the complements of the backdrop and source values, then complements the result.
/// </summary>
Screen,
/// <summary>
/// Selects the minimum of the backdrop and source values.
/// </summary>
Darken,
/// <summary>
/// Selects the max of the backdrop and source values.
/// </summary>
Lighten,
/// <summary>
/// Multiplies or screens the values, depending on the backdrop vector values.
/// </summary>
Overlay,
/// <summary>
/// Multiplies or screens the colors, depending on the source value.
/// returns the destination over the source.
/// </summary>
HardLight,
SrcOver = 0,
/// <summary>
/// returns the source colors.
/// </summary>
@ -61,22 +21,17 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// returns the source over the destination.
/// </summary>
Atop,
/// <summary>
/// returns the destination over the source.
/// </summary>
Over,
SrcAtop,
/// <summary>
/// The source where the destination and source overlap.
/// </summary>
In,
SrcIn,
/// <summary>
/// The destination where the destination and source overlap.
/// </summary>
Out,
SrcOut,
/// <summary>
/// The destination where the source does not overlap it.

56
src/ImageSharp/PixelFormats/PixelColorBlendingMode.cs

@ -0,0 +1,56 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Enumerates the various color blending modes.
/// </summary>
public enum PixelColorBlendingMode
{
/// <summary>
/// Default blending mode, also known as "Normal" or "Alpha Blending"
/// </summary>
Normal = 0,
/// <summary>
/// Blends the 2 values by multiplication.
/// </summary>
Multiply,
/// <summary>
/// Blends the 2 values by addition.
/// </summary>
Add,
/// <summary>
/// Blends the 2 values by subtraction.
/// </summary>
Subtract,
/// <summary>
/// Multiplies the complements of the backdrop and source values, then complements the result.
/// </summary>
Screen,
/// <summary>
/// Selects the minimum of the backdrop and source values.
/// </summary>
Darken,
/// <summary>
/// Selects the max of the backdrop and source values.
/// </summary>
Lighten,
/// <summary>
/// Multiplies or screens the values, depending on the backdrop vector values.
/// </summary>
Overlay,
/// <summary>
/// Multiplies or screens the colors, depending on the source value.
/// </summary>
HardLight,
}
}

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

@ -1,50 +1,217 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
/// Provides access to pixel blenders
/// </content>
public partial class PixelOperations<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="mode">The blending mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelBlenderMode mode)
{
switch (mode)
{
case PixelBlenderMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance;
case PixelBlenderMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance;
case PixelBlenderMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance;
case PixelBlenderMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance;
case PixelBlenderMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance;
case PixelBlenderMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance;
case PixelBlenderMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance;
case PixelBlenderMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance;
case PixelBlenderMode.Src: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance;
case PixelBlenderMode.Atop: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance;
case PixelBlenderMode.Over: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
case PixelBlenderMode.In: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance;
case PixelBlenderMode.Out: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance;
case PixelBlenderMode.Dest: return DefaultPixelBlenders<TPixel>.NormalDest.Instance;
case PixelBlenderMode.DestAtop: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance;
case PixelBlenderMode.DestOver: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance;
case PixelBlenderMode.DestIn: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance;
case PixelBlenderMode.DestOut: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance;
case PixelBlenderMode.Clear: return DefaultPixelBlenders<TPixel>.NormalClear.Instance;
case PixelBlenderMode.Xor: return DefaultPixelBlenders<TPixel>.NormalXor.Instance;
case PixelBlenderMode.Normal:
default:
return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
/// Provides access to pixel blenders
/// </content>
public partial class PixelOperations<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="options">the blending and composition to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options)
{
return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode);
}
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="colorMode">The color blending mode to apply</param>
/// <param name="alphaMode">The alpha composition mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
{
switch (alphaMode)
{
case PixelAlphaCompositionMode.Clear:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyClear.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddClear.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractClear.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenClear.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenClear.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenClear.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayClear.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightClear.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalClear.Instance;
}
case PixelAlphaCompositionMode.Xor:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyXor.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddXor.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractXor.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenXor.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenXor.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenXor.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayXor.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightXor.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalXor.Instance;
}
case PixelAlphaCompositionMode.Src:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrc.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrc.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrc.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrc.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrc.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrc.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrc.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrc.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance;
}
case PixelAlphaCompositionMode.SrcAtop:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcAtop.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance;
}
case PixelAlphaCompositionMode.SrcIn:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcIn.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance;
}
case PixelAlphaCompositionMode.SrcOut:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOut.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance;
}
case PixelAlphaCompositionMode.Dest:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDest.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDest.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDest.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDest.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDest.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDest.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDest.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDest.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDest.Instance;
}
case PixelAlphaCompositionMode.DestAtop:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestAtop.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance;
}
case PixelAlphaCompositionMode.DestIn:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestIn.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance;
}
case PixelAlphaCompositionMode.DestOut:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOut.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance;
}
case PixelAlphaCompositionMode.DestOver:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOver.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance;
}
case PixelAlphaCompositionMode.SrcOver:
default:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
}
}
}
}
}

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

@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
amountSpan[i] = this.GraphicsOptions.BlendPercentage;
}
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(this.GraphicsOptions.BlenderMode);
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(this.GraphicsOptions);
ParallelFor.WithConfiguration(
minY,
maxY,

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

@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
{
this.GlowColor = color;
this.Radius = radius;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
this.GraphicsOptions = options;
}

4
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
{
this.VignetteColor = color;
this.GraphicsOptions = options;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
}
/// <summary>
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
this.VignetteColor = color;
this.RadiusX = radiusX;
this.RadiusY = radiusY;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
this.GraphicsOptions = options;
}

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

@ -25,30 +25,30 @@ namespace SixLabors.ImageSharp.Tests
};
[Theory]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Multiply)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Add)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Subtract)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Screen)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Darken)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Lighten)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Overlay)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.HardLight)]
public void ImageShouldApplyDrawImage<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Multiply)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Add)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Subtract)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Screen)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Darken)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Lighten)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Overlay)]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.HardLight)]
public void ImageShouldApplyDrawImage<TPixel>(TestImageProvider<TPixel> provider, PixelColorBlendingMode mode)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes))
{
blend.Mutate(x => x.Resize(image.Width / 2, image.Height / 2));
image.Mutate(x => x.DrawImage(blend, mode, .75f, new Point(image.Width / 4, image.Height / 4)));
image.Mutate(x => x.DrawImage(blend, new Point(image.Width / 4, image.Height / 4), mode, .75f) );
image.DebugSave(provider, new { mode });
}
}
[Theory]
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)]
public void ImageShouldDrawTransformedImage<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)]
public void ImageShouldDrawTransformedImage<TPixel>(TestImageProvider<TPixel> provider, PixelColorBlendingMode mode)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests
new Rectangle(0, 0, destBounds.Width, destBounds.Height)));
var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2);
image.Mutate(x => x.DrawImage(blend, mode, .75F, position));
image.Mutate(x => x.DrawImage(blend, position, mode, .75F));
image.DebugSave(provider, new[] { "Transformed" });
}
}
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests
Rgba32 backgroundPixel = background[0, 0];
Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1];
background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Point(xy, xy)));
background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F));
Assert.Equal(Rgba32.White, backgroundPixel);
Assert.Equal(overlayPixel, background[0, 0]);
@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests
Rgba32 backgroundPixel = background[xy - 1, xy - 1];
Rgba32 overlayPixel = overlay[0, 0];
background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Point(xy, xy)));
background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F));
Assert.Equal(Rgba32.White, backgroundPixel);
Assert.Equal(overlayPixel, background[xy, xy]);

68
tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs

@ -98,38 +98,38 @@ namespace SixLabors.ImageSharp.Tests.Drawing
useReferenceOutputFrom: nameof(this.FillRegion));
}
public static readonly TheoryData<bool, string, float, PixelBlenderMode, float> BlendData =
new TheoryData<bool, string, float, PixelBlenderMode, float>()
public static readonly TheoryData<bool, string, float, PixelColorBlendingMode, float> BlendData =
new TheoryData<bool, string, float, PixelColorBlendingMode, float>()
{
{ false, "Blue", 0.5f, PixelBlenderMode.Normal, 1.0f },
{ false, "Blue", 1.0f, PixelBlenderMode.Normal, 0.5f },
{ false, "Green", 0.5f, PixelBlenderMode.Normal, 0.3f },
{ false, "HotPink", 0.8f, PixelBlenderMode.Normal, 0.8f },
{ false, "Blue", 0.5f, PixelBlenderMode.Multiply, 1.0f },
{ false, "Blue", 1.0f, PixelBlenderMode.Multiply, 0.5f },
{ false, "Green", 0.5f, PixelBlenderMode.Multiply, 0.3f },
{ false, "HotPink", 0.8f, PixelBlenderMode.Multiply, 0.8f },
{ false, "Blue", 0.5f, PixelBlenderMode.Add, 1.0f },
{ false, "Blue", 1.0f, PixelBlenderMode.Add, 0.5f },
{ false, "Green", 0.5f, PixelBlenderMode.Add, 0.3f },
{ false, "HotPink", 0.8f, PixelBlenderMode.Add, 0.8f },
{ true, "Blue", 0.5f, PixelBlenderMode.Normal, 1.0f },
{ true, "Blue", 1.0f, PixelBlenderMode.Normal, 0.5f },
{ true, "Green", 0.5f, PixelBlenderMode.Normal, 0.3f },
{ true, "HotPink", 0.8f, PixelBlenderMode.Normal, 0.8f },
{ true, "Blue", 0.5f, PixelBlenderMode.Multiply, 1.0f },
{ true, "Blue", 1.0f, PixelBlenderMode.Multiply, 0.5f },
{ true, "Green", 0.5f, PixelBlenderMode.Multiply, 0.3f },
{ true, "HotPink", 0.8f, PixelBlenderMode.Multiply, 0.8f },
{ true, "Blue", 0.5f, PixelBlenderMode.Add, 1.0f },
{ true, "Blue", 1.0f, PixelBlenderMode.Add, 0.5f },
{ true, "Green", 0.5f, PixelBlenderMode.Add, 0.3f },
{ true, "HotPink", 0.8f, PixelBlenderMode.Add, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f },
};
[Theory]
@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
bool triggerFillRegion,
string newColorName,
float alpha,
PixelBlenderMode blenderMode,
PixelColorBlendingMode blenderMode,
float blendPercentage)
where TPixel : struct, IPixel<TPixel>
{
@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var options = new GraphicsOptions(false)
{
BlenderMode = blenderMode,
ColorBlendingMode = blenderMode,
BlendPercentage = blendPercentage
};
@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(blenderMode);
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(blenderMode, PixelAlphaCompositionMode.SrcOver);
TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage);
image.ComparePixelBufferTo(expectedPixel);

65
tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs

@ -15,14 +15,26 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[GroupOutput("Drawing")]
public class SolidFillBlendedShapesTests
{
public static IEnumerable<object[]> modes =
((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))).Select(x => new object[] { x });
public static IEnumerable<object[]> modes = GetAllModeCombinations();
private static IEnumerable<object[]> GetAllModeCombinations()
{
foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode)))
{
foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode)))
{
yield return new object[] { blending, composition };
}
}
}
[Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect<TPixel>(
TestImageProvider<TPixel> provider,
PixelBlenderMode mode)
PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
@ -34,12 +46,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
NamedColors<TPixel>.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)
)
.Fill(new GraphicsOptions(true) { BlenderMode = mode },
.Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition },
NamedColors<TPixel>.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))
);
VerifyImage(provider, mode, img);
VerifyImage(provider, blending, composition, img);
}
}
@ -47,7 +59,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse<TPixel>(
TestImageProvider<TPixel> provider,
PixelBlenderMode mode)
PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
@ -60,17 +73,17 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode },
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
NamedColors<TPixel>.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode },
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
NamedColors<TPixel>.Transparent,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
);
VerifyImage(provider, mode, img);
VerifyImage(provider, blending, composition, img);
}
}
@ -78,7 +91,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse<TPixel>(
TestImageProvider<TPixel> provider,
PixelBlenderMode mode)
PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
@ -91,7 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode },
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
NamedColors<TPixel>.HotPink,
new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY)));
var c = NamedColors<TPixel>.Red.ToVector4();
@ -101,18 +115,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { BlenderMode = mode },
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
pixel,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
);
VerifyImage(provider, mode, img); ;
VerifyImage(provider, blending, composition, img); ;
}
}
[Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>(
TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel>
{
using(Image<TPixel> dstImg = provider.GetImage(), srcImg = provider.GetImage())
@ -131,28 +148,32 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
dstImg.Mutate(
x => x.DrawImage(new GraphicsOptions(true) { BlenderMode = mode }, srcImg)
x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition })
);
VerifyImage(provider, mode, dstImg);
VerifyImage(provider, blending, composition, dstImg);
}
}
private static void VerifyImage<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode, Image<TPixel> img)
private static void VerifyImage<TPixel>(
TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition,
Image<TPixel> img)
where TPixel : struct, IPixel<TPixel>
{
img.DebugSave(
provider,
new { mode },
new { composition, blending },
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
var comparer = ImageComparer.TolerantPercentage(0.01f, 3);
img.CompareFirstFrameToReferenceOutput(comparer,
img.CompareFirstFrameToReferenceOutput(comparer,
provider,
new { mode },
new { composition, blending },
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
appendSourceFileOrDescription: false);
}
}
}

101
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs

@ -1,47 +1,56 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
{
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
public class PorterDuffCompositorTests
{
// TODO: Add other modes to compare.
public static readonly TheoryData<PixelBlenderMode> CompositingOperators =
new TheoryData<PixelBlenderMode>
{
PixelBlenderMode.Src,
PixelBlenderMode.Atop,
PixelBlenderMode.Over,
PixelBlenderMode.In,
PixelBlenderMode.Out,
PixelBlenderMode.Dest,
PixelBlenderMode.DestAtop,
PixelBlenderMode.DestOver,
PixelBlenderMode.DestIn,
PixelBlenderMode.DestOut,
PixelBlenderMode.Clear,
PixelBlenderMode.Xor
};
[Theory]
[WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)]
public void PorterDuffOutputIsCorrect(TestImageProvider<Rgba32> provider, PixelBlenderMode mode)
{
var srcFile = TestFile.Create(TestImages.Png.PDSrc);
using (Image<Rgba32> src = srcFile.CreateImage())
using (Image<Rgba32> dest = provider.GetImage())
{
using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(new GraphicsOptions { BlenderMode = mode }, src)))
{
res.DebugSave(provider, mode.ToString());
res.CompareToReferenceOutput(provider, mode.ToString());
}
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
{
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
public class PorterDuffCompositorTests
{
// TODO: Add other modes to compare.
public static readonly TheoryData<PixelAlphaCompositionMode> CompositingOperators =
new TheoryData<PixelAlphaCompositionMode>
{
PixelAlphaCompositionMode.Src,
PixelAlphaCompositionMode.SrcAtop,
PixelAlphaCompositionMode.SrcOver,
PixelAlphaCompositionMode.SrcIn,
PixelAlphaCompositionMode.SrcOut,
PixelAlphaCompositionMode.Dest,
PixelAlphaCompositionMode.DestAtop,
PixelAlphaCompositionMode.DestOver,
PixelAlphaCompositionMode.DestIn,
PixelAlphaCompositionMode.DestOut,
PixelAlphaCompositionMode.Clear,
PixelAlphaCompositionMode.Xor
};
[Theory]
[WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)]
public void PorterDuffOutputIsCorrect(TestImageProvider<Rgba32> provider, PixelAlphaCompositionMode mode)
{
var srcFile = TestFile.Create(TestImages.Png.PDSrc);
using (Image<Rgba32> src = srcFile.CreateImage())
using (Image<Rgba32> dest = provider.GetImage())
{
GraphicsOptions options = new GraphicsOptions
{
AlphaCompositionMode = mode
};
using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options)))
{
string combinedMode = mode.ToString();
if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3);
res.DebugSave(provider, combinedMode);
res.CompareToReferenceOutput(provider, combinedMode);
}
}
}
}
}

74
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs

@ -12,64 +12,36 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.PixelFormats
{
public class PixelBlenderTests
{
public static TheoryData<object, Type, PixelBlenderMode> BlenderMappings = new TheoryData<object, Type, PixelBlenderMode>()
{
public static TheoryData<object, Type, PixelColorBlendingMode> BlenderMappings = new TheoryData<object, Type, PixelColorBlendingMode>()
{
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelBlenderMode.Normal },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcOver), PixelBlenderMode.Screen },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcOver), PixelBlenderMode.HardLight },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcOver), PixelBlenderMode.Overlay },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcOver), PixelBlenderMode.Darken },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.LightenSrcOver), PixelBlenderMode.Lighten },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.AddSrcOver), PixelBlenderMode.Add },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcOver), PixelBlenderMode.Subtract },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcOver), PixelBlenderMode.Multiply },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrc), PixelBlenderMode.Src },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcAtop), PixelBlenderMode.Atop },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelBlenderMode.Over },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcIn), PixelBlenderMode.In },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOut), PixelBlenderMode.Out },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDest), PixelBlenderMode.Dest },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestAtop), PixelBlenderMode.DestAtop },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestOver), PixelBlenderMode.DestOver },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestIn), PixelBlenderMode.DestIn },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalDestOut), PixelBlenderMode.DestOut },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalClear), PixelBlenderMode.Clear },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalXor), PixelBlenderMode.Xor },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOver), PixelBlenderMode.Normal },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.ScreenSrcOver), PixelBlenderMode.Screen },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.HardLightSrcOver), PixelBlenderMode.HardLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.OverlaySrcOver), PixelBlenderMode.Overlay },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DarkenSrcOver), PixelBlenderMode.Darken },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.LightenSrcOver), PixelBlenderMode.Lighten },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.AddSrcOver), PixelBlenderMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.SubtractSrcOver), PixelBlenderMode.Subtract },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelBlenderMode.Multiply },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrc), PixelBlenderMode.Src },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcAtop), PixelBlenderMode.Atop },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOver), PixelBlenderMode.Over },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcIn), PixelBlenderMode.In },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOut), PixelBlenderMode.Out },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDest), PixelBlenderMode.Dest },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestAtop), PixelBlenderMode.DestAtop },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestOver), PixelBlenderMode.DestOver },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestIn), PixelBlenderMode.DestIn },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalDestOut), PixelBlenderMode.DestOut },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalClear), PixelBlenderMode.Clear },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalXor), PixelBlenderMode.Xor },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelColorBlendingMode.Normal },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.ScreenSrcOver), PixelColorBlendingMode.Screen },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.HardLightSrcOver), PixelColorBlendingMode.HardLight },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.OverlaySrcOver), PixelColorBlendingMode.Overlay },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.DarkenSrcOver), PixelColorBlendingMode.Darken },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.LightenSrcOver), PixelColorBlendingMode.Lighten },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.AddSrcOver), PixelColorBlendingMode.Add },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.SubtractSrcOver), PixelColorBlendingMode.Subtract },
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.MultiplySrcOver), PixelColorBlendingMode.Multiply },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.NormalSrcOver), PixelColorBlendingMode.Normal },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.ScreenSrcOver), PixelColorBlendingMode.Screen },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.HardLightSrcOver), PixelColorBlendingMode.HardLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.OverlaySrcOver), PixelColorBlendingMode.Overlay },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.DarkenSrcOver), PixelColorBlendingMode.Darken },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.LightenSrcOver), PixelColorBlendingMode.Lighten },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.AddSrcOver), PixelColorBlendingMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.SubtractSrcOver), PixelColorBlendingMode.Subtract },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelColorBlendingMode.Multiply },
};
[Theory]
[MemberData(nameof(BlenderMappings))]
public void ReturnsCorrectBlender<TPixel>(TestPixel<TPixel> pixel, Type type, PixelBlenderMode mode)
public void ReturnsCorrectBlender<TPixel>(TestPixel<TPixel> pixel, Type type, PixelColorBlendingMode mode)
where TPixel : struct, IPixel<TPixel>
{
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode);
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver);
Assert.IsType(type, blender);
}
}

11
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs

@ -88,6 +88,17 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
where TPixel : struct, IPixel<TPixel>
{
Assert.NotNull(PixelOperations<TPixel>.Instance);
}
[Fact]
public void IsOpaqueColor()
{
Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red));
Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red));
Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Transparent));
Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red));
Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal,PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red));
}
}

2
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -365,7 +365,7 @@ namespace SixLabors.ImageSharp.Tests
if (!File.Exists(referenceOutputFile))
{
throw new Exception("Reference output file missing: " + referenceOutputFile);
throw new System.IO.FileNotFoundException("Reference output file missing: " + referenceOutputFile, referenceOutputFile);
}
IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile);

2
tests/Images/External

@ -1 +1 @@
Subproject commit 825220cdc4e9d1b4b3b474c63139e18e1cdb800e
Subproject commit 6a43d335f216d6325a6a9fd8d35942ade12b7c7b
Loading…
Cancel
Save