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++)
{
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
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);

100
src/ImageSharp.Drawing/DrawImage.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using Drawing.Processors;
using ImageSharp.Drawing;
using ImageSharp.PixelFormats;
/// <summary>
@ -14,31 +15,17 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Blend<TPixel>(this Image<TPixel> source, Image<TPixel> image, int percent = 50)
where TPixel : struct, IPixel<TPixel>
{
return DrawImage(source, image, percent, default(Size), default(Point));
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, int percent, Size size, Point location)
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, Size size, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
if (size == default(Size))
@ -51,27 +38,56 @@ namespace ImageSharp
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;
}
/// <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="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>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <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="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> 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>
{
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>
@ -79,27 +95,37 @@ namespace ImageSharp
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="pixelFunc">Pixel function effect to apply on every pixel</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="size">The size to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, Func<TPixel, TPixel, float, TPixel> pixelFunc, int percent, Size size, Point location)
public static Image<TPixel> DrawImage<TPixel>(this Image<TPixel> source, Image<TPixel> image, float percent, Size size, Point location)
where TPixel : struct, IPixel<TPixel>
{
if (size == default(Size))
{
size = new Size(image.Width, image.Height);
}
if (location == default(Point))
{
location = Point.Empty;
}
GraphicsOptions options = GraphicsOptions.Default;
options.BlendPercentage = percent;
return source.DrawImage(image, size, location, options);
}
source.ApplyProcessor(new DrawImageEffectProcessor<TPixel>(image, size, location, pixelFunc, percent), source.Bounds);
return source;
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <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>
public GraphicsOptions(bool enableAntialiasing)
{
this.blenderMode = PixelBlenderMode.Default;
this.blenderMode = PixelBlenderMode.Normal;
this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
@ -64,6 +64,10 @@ namespace ImageSharp.Drawing
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>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary>

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

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

51
src/ImageSharp/PixelFormats/PixelBlenderMode.cs

@ -15,69 +15,48 @@ namespace ImageSharp.PixelFormats
public enum PixelBlenderMode
{
/// <summary>
/// The default composition mode.
/// Default blending mode, also known as "Normal" or "Alpha Blending"
/// </summary>
/// <remarks> uses PremultipliedLerpTransform </remarks>
Default = 0,
Normal = 0,
/// <summary>
/// Normal transform.
/// </summary>
Normal,
/// <summary>
/// Multiply Transform.
/// Backdrop + Source
/// </summary>
Multiply,
/// <summary>
/// Screen Transform.
/// Backdrop + Source
/// </summary>
Screen,
Add,
/// <summary>
/// HardLight Transform.
/// Backdrop - Source
/// </summary>
HardLight,
Substract,
/// <summary>
/// Overlay Transform.
/// Screen effect
/// </summary>
Overlay,
Screen,
/// <summary>
/// Darken Transform.
/// Darken effect
/// </summary>
Darken,
/// <summary>
/// Lighten Transform.
/// Lighten effect
/// </summary>
Lighten,
/// <summary>
/// SoftLight Transform.
/// </summary>
SoftLight,
/// <summary>
/// Dodge Transform.
/// </summary>
Dodge,
/// <summary>
/// Burn Transform.
/// Overlay effect
/// </summary>
Burn,
/// <summary>
/// Difference Transform.
/// </summary>
Difference,
Overlay,
/// <summary>
/// Exclusion Transform.
/// Hard light effect
/// </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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -13,16 +13,18 @@ namespace ImageSharp.PixelFormats.PixelBlenders
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <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>
{
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultAddPixelBlender<TPixel> Instance { get; } = new DefaultAddPixelBlender<TPixel>();
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Burn(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
return PorterDuffFunctions<TPixel>.AddFunction(background, source, amount);
}
/// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Burn(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
destination[i] = PorterDuffFunctions<TPixel>.AddFunction(destination[i], source[i], amount[i]);
}
}
}

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

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultDarkenPixelBlender<TPixel> : PixelBlender<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 />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Darken(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
return PorterDuffFunctions<TPixel>.DarkenFunction(background, source, amount);
}
/// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Darken(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
destination[i] = PorterDuffFunctions<TPixel>.DarkenFunction(destination[i], source[i], amount[i]);
}
}
}

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

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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultLightenPixelBlender<TPixel> : PixelBlender<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 />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Lighten(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
return PorterDuffFunctions<TPixel>.LightenFunction(background, source, amount);
}
/// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Lighten(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
destination[i] = PorterDuffFunctions<TPixel>.LightenFunction(destination[i], source[i], amount[i]);
}
}
}

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

@ -16,13 +16,15 @@ namespace ImageSharp.PixelFormats.PixelBlenders
internal class DefaultMultiplyPixelBlender<TPixel> : PixelBlender<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 />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.Multiply(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
return PorterDuffFunctions<TPixel>.MultiplyFunction(background, source, amount);
}
/// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.Multiply(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
destination[i] = PorterDuffFunctions<TPixel>.MultiplyFunction(destination[i], source[i], amount[i]);
}
}
}

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

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

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

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.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -13,16 +13,18 @@ namespace ImageSharp.PixelFormats.PixelBlenders
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <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>
{
/// <summary>
/// Gets the static instance of this blender.
/// </summary>
public static DefaultSubstractPixelBlender<TPixel> Instance { get; } = new DefaultSubstractPixelBlender<TPixel>();
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
Vector4 result = Vector4BlendTransforms.SoftLight(background.ToVector4(), source.ToVector4());
TPixel resultPixel = default(TPixel);
resultPixel.PackFromVector4(result);
return resultPixel;
return PorterDuffFunctions<TPixel>.SubstractFunction(background, source, amount);
}
/// <inheritdoc />
@ -34,8 +36,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
for (int i = 0; i < destination.Length; i++)
{
Vector4 result = Vector4BlendTransforms.SoftLight(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
destination[i] = PorterDuffFunctions<TPixel>.SubstractFunction(destination[i], source[i], amount[i]);
}
}
}

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

@ -34,56 +34,6 @@ namespace ImageSharp.PixelFormats
/// </summary>
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>
/// Find an instance of the pixel blender.
/// </summary>
@ -93,32 +43,25 @@ namespace ImageSharp.PixelFormats
{
switch (mode)
{
case PixelBlenderMode.Normal:
return this.normalBlender;
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:
return this.screenBlender;
case PixelBlenderMode.HardLight:
return this.hardLightBlender;
case PixelBlenderMode.Overlay:
return this.overlayBlender;
return DefaultScreenPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Darken:
return this.darkenBlender;
return DefaultDarkenPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Lighten:
return this.lightenBlender;
case PixelBlenderMode.SoftLight:
return this.softLightBlender;
case PixelBlenderMode.Dodge:
return this.dodgeBlender;
case PixelBlenderMode.Burn:
return this.burnBlender;
case PixelBlenderMode.Difference:
return this.differenceBlender;
case PixelBlenderMode.Exclusion:
return this.exclusionBlender;
return DefaultLightenPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Overlay:
return DefaultOverlayPixelBlender<TPixel>.Instance;
case PixelBlenderMode.HardLight:
return DefaultHardLightPixelBlender<TPixel>.Instance;
case PixelBlenderMode.Normal:
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>
/// Collection of Porter Duff alpha blending functions
/// </summary>
/// <typeparam name="TBckPixel">Backdrop Pixel Format</typeparam>
/// <typeparam name="TsrcPixel">Source Pixel Format</typeparam>
/// <typeparam name="TPixel">Pixel Format</typeparam>
/// <remarks>
/// These functions are designed to be a general solution for all color cases,
/// that is, they take in account the alpha value of both the backdrop
@ -21,9 +20,8 @@ namespace ImageSharp.PixelFormats
/// Note there are faster functions for when the backdrop color is known
/// to be opaque
/// </remarks>
internal static class PorterDuffFunctions<TBckPixel, TsrcPixel>
where TBckPixel : IPixel
where TsrcPixel : IPixel
internal static class PorterDuffFunctions<TPixel>
where TPixel : IPixel
{
/// <summary>
/// Source over backdrop
@ -32,7 +30,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel NormalBlendFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -53,7 +52,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel MultiplyFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -74,7 +74,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel AddFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -95,7 +96,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel SubstractFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -116,7 +118,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel ScreenFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -137,7 +140,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel DarkenFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -158,7 +162,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel LightenFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -179,7 +184,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel OverlayFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -204,7 +210,8 @@ namespace ImageSharp.PixelFormats
/// <param name="source">Source color</param>
/// <param name="opacity">Opacity applied to Source Alpha</param>
/// <returns>Output color</returns>
public static TBckPixel HardLightFunction(TBckPixel backdrop, TsrcPixel source, float opacity)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity)
{
Vector4 l = source.ToVector4();
l.W *= opacity;
@ -242,7 +249,7 @@ namespace ImageSharp.PixelFormats
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
/// <returns>The final color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static TBckPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform)
private static TPixel Compose(Vector4 backdrop, Vector4 source, Vector4 xform)
{
DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W));
@ -258,7 +265,7 @@ namespace ImageSharp.PixelFormats
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a;
xform.W = a;
TBckPixel packed = default(TBckPixel);
TPixel packed = default(TPixel);
packed.PackFromVector4(xform);
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.
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++)
{
int offsetY = y - startY;
int offsetX = minX - startX;
for (int i = 0; i < width; i++)
{
float distance = Vector2.Distance(centre, new Vector2((i + offsetX), offsetY));
amounts[i] = 1 - (.95F * (distance / maxDistance));
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
amounts[i] = (1 - (.95F * (distance / maxDistance))).Clamp(0, 1);
}
BufferSpan<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width);
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;
}
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Default);
Parallel.For(
minY,
maxY,

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

@ -6,6 +6,7 @@
namespace ImageSharp.Tests
{
using System.IO;
using ImageSharp.Drawing;
using ImageSharp.PixelFormats;
using Xunit;
@ -16,7 +17,7 @@ namespace ImageSharp.Tests
{
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())
{
@ -24,14 +25,17 @@ namespace ImageSharp.Tests
{
using (Image image = file.CreateImage())
{
foreach (PixelTransformMode mode in modes)
foreach (PixelBlenderMode mode in modes)
{
using (FileStream output = File.OpenWrite($"{path}/{mode}.{file.FileName}"))
{
Size size = new Size(image.Width / 2, image.Height / 2);
Point loc = new Point(image.Width / 4, image.Height / 4);
image.DrawImage(blend, mode, 75, size, loc).Save(output);
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}"))
{
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);
}
}

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>()
{
{ new TestPixel<Rgba32>(), typeof(DefaultPremultipliedLerpPixelBlender<Rgba32>), PixelBlenderMode.Default },
{ new TestPixel<Rgba32>(), typeof(DefaultNormalPixelBlender<Rgba32>), PixelBlenderMode.Normal },
{ new TestPixel<Rgba32>(), typeof(DefaultScreenPixelBlender<Rgba32>), PixelBlenderMode.Screen },
{ new TestPixel<Rgba32>(), typeof(DefaultHardLightPixelBlender<Rgba32>), PixelBlenderMode.HardLight },
{ new TestPixel<Rgba32>(), typeof(DefaultOverlayPixelBlender<Rgba32>), PixelBlenderMode.Overlay },
{ new TestPixel<Rgba32>(), typeof(DefaultDarkenPixelBlender<Rgba32>), PixelBlenderMode.Darken },
{ new TestPixel<Rgba32>(), typeof(DefaultLightenPixelBlender<Rgba32>), PixelBlenderMode.Lighten },
{ new TestPixel<Rgba32>(), typeof(DefaultSoftLightPixelBlender<Rgba32>), PixelBlenderMode.SoftLight },
{ new TestPixel<Rgba32>(), typeof(DefaultDodgePixelBlender<Rgba32>), PixelBlenderMode.Dodge },
{ new TestPixel<Rgba32>(), typeof(DefaultBurnPixelBlender<Rgba32>), PixelBlenderMode.Burn },
{ new TestPixel<Rgba32>(), typeof(DefaultDifferencePixelBlender<Rgba32>), PixelBlenderMode.Difference },
{ new TestPixel<Rgba32>(), typeof(DefaultExclusionPixelBlender<Rgba32>), PixelBlenderMode.Exclusion },
{ new TestPixel<Rgba32>(), typeof(DefaultAddPixelBlender<Rgba32>), PixelBlenderMode.Add },
{ new TestPixel<Rgba32>(), typeof(DefaultSubstractPixelBlender<Rgba32>), PixelBlenderMode.Substract },
{ new TestPixel<Rgba32>(), typeof(DefaultMultiplyPixelBlender<Rgba32>), PixelBlenderMode.Multiply },
{ new TestPixel<RgbaVector>(), typeof(DefaultPremultipliedLerpPixelBlender<RgbaVector>), PixelBlenderMode.Default },
{ new TestPixel<RgbaVector>(), typeof(DefaultNormalPixelBlender<RgbaVector>), PixelBlenderMode.Normal },
{ new TestPixel<RgbaVector>(), typeof(DefaultScreenPixelBlender<RgbaVector>), PixelBlenderMode.Screen },
{ new TestPixel<RgbaVector>(), typeof(DefaultHardLightPixelBlender<RgbaVector>), PixelBlenderMode.HardLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultOverlayPixelBlender<RgbaVector>), PixelBlenderMode.Overlay },
{ new TestPixel<RgbaVector>(), typeof(DefaultDarkenPixelBlender<RgbaVector>), PixelBlenderMode.Darken },
{ new TestPixel<RgbaVector>(), typeof(DefaultLightenPixelBlender<RgbaVector>), PixelBlenderMode.Lighten },
{ new TestPixel<RgbaVector>(), typeof(DefaultSoftLightPixelBlender<RgbaVector>), PixelBlenderMode.SoftLight },
{ new TestPixel<RgbaVector>(), typeof(DefaultDodgePixelBlender<RgbaVector>), PixelBlenderMode.Dodge },
{ new TestPixel<RgbaVector>(), typeof(DefaultBurnPixelBlender<RgbaVector>), PixelBlenderMode.Burn },
{ new TestPixel<RgbaVector>(), typeof(DefaultDifferencePixelBlender<RgbaVector>), PixelBlenderMode.Difference },
{ new TestPixel<RgbaVector>(), typeof(DefaultExclusionPixelBlender<RgbaVector>), PixelBlenderMode.Exclusion }
{ new TestPixel<RgbaVector>(), typeof(DefaultAddPixelBlender<RgbaVector>), PixelBlenderMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultSubstractPixelBlender<RgbaVector>), PixelBlenderMode.Substract },
{ new TestPixel<RgbaVector>(), typeof(DefaultMultiplyPixelBlender<RgbaVector>), PixelBlenderMode.Multiply },
};
[Theory]

Loading…
Cancel
Save