Browse Source

updates to comments and moved code

pull/202/head
Scott Williams 10 years ago
parent
commit
d56e658854
  1. 2
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  2. 2
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  3. 2
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  4. 2
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  5. 2
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  6. 18
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  7. 2
      src/ImageSharp/GraphicsOptions.cs
  8. 16
      src/ImageSharp/PixelFormats/PixelBlenderMode.cs
  9. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs
  10. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
  11. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
  12. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
  13. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
  14. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
  15. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
  16. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
  17. 6
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs
  18. 8
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  19. 27
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  20. 12
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  21. 85
      src/ImageSharp/Processing/Overlays/Glow.cs
  22. 2
      src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
  23. 42
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  24. 98
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs
  25. 87
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  26. 56
      tests/ImageSharp.Tests/Drawing/BlendedShapes.cs
  27. 56
      tests/ImageSharp.Tests/Drawing/DrawImageEffectTest.cs
  28. 36
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs

2
src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs

@ -134,7 +134,7 @@ namespace ImageSharp.Drawing.Brushes
} }
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
} }
} }
} }

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

@ -162,7 +162,7 @@ namespace ImageSharp.Drawing.Brushes
} }
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
} }
} }
} }

2
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -80,7 +80,7 @@ namespace ImageSharp.Drawing.Processors
} }
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
} }
} }
} }

2
src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs

@ -154,7 +154,7 @@ namespace ImageSharp.Drawing.Brushes
} }
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
} }
} }
} }

2
src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs

@ -98,7 +98,7 @@ namespace ImageSharp.Drawing.Brushes
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
} }
this.Blender.Compose(destinationRow, destinationRow, this.Colors, amountBuffer); this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer);
} }
} }
} }

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

@ -67,7 +67,6 @@ namespace ImageSharp.Drawing.Processors
{ {
if (targetImage.Bounds.Size != this.Size) 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); targetImage = disposableImage = new Image<TPixel>(this.Image).Resize(this.Size.Width, this.Size.Height);
} }
@ -80,7 +79,7 @@ namespace ImageSharp.Drawing.Processors
int width = maxX - minX; int width = maxX - minX;
using (Buffer<float> amount = new Buffer<float>(width)) using (Buffer<float> amount = new Buffer<float>(width))
using (PixelAccessor<TPixel> toBlendPixels = this.Image.Lock()) using (PixelAccessor<TPixel> toBlendPixels = targetImage.Lock())
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{ {
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
@ -96,19 +95,8 @@ namespace ImageSharp.Drawing.Processors
{ {
BufferSpan<TPixel> background = sourcePixels.GetRowSpan(y).Slice(minX, width); BufferSpan<TPixel> background = sourcePixels.GetRowSpan(y).Slice(minX, width);
BufferSpan<TPixel> foreground = toBlendPixels.GetRowSpan(y - minY).Slice(0, width); BufferSpan<TPixel> foreground = toBlendPixels.GetRowSpan(y - minY).Slice(0, width);
this.blender.Compose(background, background, foreground, amount); this.blender.Blend(background, background, foreground, amount);
});
// 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 finally

2
src/ImageSharp.Drawing/GraphicsOptions.cs → src/ImageSharp/GraphicsOptions.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Drawing namespace ImageSharp
{ {
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;

16
src/ImageSharp/PixelFormats/PixelBlenderMode.cs

@ -20,42 +20,42 @@ namespace ImageSharp.PixelFormats
Normal = 0, Normal = 0,
/// <summary> /// <summary>
/// Backdrop + Source /// Blends the 2 values by multiplication.
/// </summary> /// </summary>
Multiply, Multiply,
/// <summary> /// <summary>
/// Backdrop + Source /// Blends the 2 values by addition.
/// </summary> /// </summary>
Add, Add,
/// <summary> /// <summary>
/// Backdrop - Source /// Blends the 2 values by subtraction.
/// </summary> /// </summary>
Substract, Substract,
/// <summary> /// <summary>
/// Screen effect /// Multiplies the complements of the backdrop and source values, then complements the result.
/// </summary> /// </summary>
Screen, Screen,
/// <summary> /// <summary>
/// Darken effect /// Selects the minimum of the backdrop and source values.
/// </summary> /// </summary>
Darken, Darken,
/// <summary> /// <summary>
/// Lighten effect /// Selects the max of the backdrop and source values.
/// </summary> /// </summary>
Lighten, Lighten,
/// <summary> /// <summary>
/// Overlay effect /// Multiplies or screens the values, depending on the backdrop vector values.
/// </summary> /// </summary>
Overlay, Overlay,
/// <summary> /// <summary>
/// Hard light effect /// Multiplies or screens the colors, depending on the source value.
/// </summary> /// </summary>
HardLight HardLight
} }

6
src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Add" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultAddPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultAddPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultAddPixelBlender<TPixel> Instance { get; } = new DefaultAddPixelBlender<TPixel>(); public static DefaultAddPixelBlender<TPixel> Instance { get; } = new DefaultAddPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.AddFunction(background, source, amount); return PorterDuffFunctions<TPixel>.AddFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Darken" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultDarkenPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultDarkenPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultDarkenPixelBlender<TPixel> Instance { get; } = new DefaultDarkenPixelBlender<TPixel>(); public static DefaultDarkenPixelBlender<TPixel> Instance { get; } = new DefaultDarkenPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.DarkenFunction(background, source, amount); return PorterDuffFunctions<TPixel>.DarkenFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Hard Light" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultHardLightPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultHardLightPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultHardLightPixelBlender<TPixel> Instance { get; } = new DefaultHardLightPixelBlender<TPixel>(); public static DefaultHardLightPixelBlender<TPixel> Instance { get; } = new DefaultHardLightPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.HardLightFunction(background, source, amount); return PorterDuffFunctions<TPixel>.HardLightFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Lighten" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultLightenPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultLightenPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultLightenPixelBlender<TPixel> Instance { get; } = new DefaultLightenPixelBlender<TPixel>(); public static DefaultLightenPixelBlender<TPixel> Instance { get; } = new DefaultLightenPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.LightenFunction(background, source, amount); return PorterDuffFunctions<TPixel>.LightenFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Multiply" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultMultiplyPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultMultiplyPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultMultiplyPixelBlender<TPixel> Instance { get; } = new DefaultMultiplyPixelBlender<TPixel>(); public static DefaultMultiplyPixelBlender<TPixel> Instance { get; } = new DefaultMultiplyPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.MultiplyFunction(background, source, amount); return PorterDuffFunctions<TPixel>.MultiplyFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies a "Normal" otherwise nown as "Alpha Blending" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultNormalPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultNormalPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultNormalPixelBlender<TPixel> Instance { get; } = new DefaultNormalPixelBlender<TPixel>(); public static DefaultNormalPixelBlender<TPixel> Instance { get; } = new DefaultNormalPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.NormalBlendFunction(background, source, amount); return PorterDuffFunctions<TPixel>.NormalBlendFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Overlay" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultOverlayPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultOverlayPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultOverlayPixelBlender<TPixel> Instance { get; } = new DefaultOverlayPixelBlender<TPixel>(); public static DefaultOverlayPixelBlender<TPixel> Instance { get; } = new DefaultOverlayPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.OverlayFunction(background, source, amount); return PorterDuffFunctions<TPixel>.OverlayFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

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

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Screen" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultScreenPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultScreenPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultScreenPixelBlender<TPixel> Instance { get; } = new DefaultScreenPixelBlender<TPixel>(); public static DefaultScreenPixelBlender<TPixel> Instance { get; } = new DefaultScreenPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.ScreenFunction(background, source, amount); return PorterDuffFunctions<TPixel>.ScreenFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

6
src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs

@ -10,7 +10,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Applies an "Subtract" blending to pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultSubstractPixelBlender<TPixel> : PixelBlender<TPixel> internal class DefaultSubstractPixelBlender<TPixel> : PixelBlender<TPixel>
@ -22,13 +22,13 @@ namespace ImageSharp.PixelFormats.PixelBlenders
public static DefaultSubstractPixelBlender<TPixel> Instance { get; } = new DefaultSubstractPixelBlender<TPixel>(); public static DefaultSubstractPixelBlender<TPixel> Instance { get; } = new DefaultSubstractPixelBlender<TPixel>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
return PorterDuffFunctions<TPixel>.SubstractFunction(background, source, amount); return PorterDuffFunctions<TPixel>.SubstractFunction(background, source, amount);
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount) public override void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount)
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));

8
src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs

@ -13,7 +13,7 @@ namespace ImageSharp.PixelFormats
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Composes 2 pixels together. /// Blend 2 pixels together.
/// </summary> /// </summary>
/// <param name="background">The background color.</param> /// <param name="background">The background color.</param>
/// <param name="source">The source color.</param> /// <param name="source">The source color.</param>
@ -22,10 +22,10 @@ namespace ImageSharp.PixelFormats
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
/// <returns>The final pixel value after composition</returns> /// <returns>The final pixel value after composition</returns>
public abstract TPixel Compose(TPixel background, TPixel source, float amount); public abstract TPixel Blend(TPixel background, TPixel source, float amount);
/// <summary> /// <summary>
/// Composes 2 pixels together. /// Blend 2 pixels together.
/// </summary> /// </summary>
/// <param name="destination">The destination span.</param> /// <param name="destination">The destination span.</param>
/// <param name="background">The background span.</param> /// <param name="background">The background span.</param>
@ -34,6 +34,6 @@ namespace ImageSharp.PixelFormats
/// A value between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
public abstract void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount); public abstract void Blend(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount);
} }
} }

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

@ -5,35 +5,14 @@
namespace ImageSharp.PixelFormats namespace ImageSharp.PixelFormats
{ {
using System.Numerics;
using System.Runtime.CompilerServices;
using ImageSharp.PixelFormats.PixelBlenders; using ImageSharp.PixelFormats.PixelBlenders;
#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag /// <content>
/// <summary> /// Provides access to pixel blenders
/// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// </content>
/// for pixel buffers of type <typeparamref name="TPixel"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public partial class PixelOperations<TPixel> public partial class PixelOperations<TPixel>
#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary>
/// Gets the NormalBlender.
/// </summary>
private PixelBlender<TPixel> normalBlender = new DefaultNormalPixelBlender<TPixel>();
/// <summary>
/// Gets the MultiplyBlender.
/// </summary>
private PixelBlender<TPixel> multiplyBlender = new DefaultMultiplyPixelBlender<TPixel>();
/// <summary>
/// Gets the ScreenBlender.
/// </summary>
private PixelBlender<TPixel> screenBlender = new DefaultScreenPixelBlender<TPixel>();
/// <summary> /// <summary>
/// Find an instance of the pixel blender. /// Find an instance of the pixel blender.
/// </summary> /// </summary>

12
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -8,14 +8,12 @@ namespace ImageSharp.PixelFormats
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag /// <summary>
/// <summary> /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
/// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// for pixel buffers of type <typeparamref name="TPixel"/>.
/// for pixel buffers of type <typeparamref name="TPixel"/>. /// </summary>
/// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public partial class PixelOperations<TPixel> public partial class PixelOperations<TPixel>
#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>

85
src/ImageSharp/Processing/Overlays/Glow.cs

@ -25,7 +25,7 @@ namespace ImageSharp
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source) public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return Glow(source, NamedColors<TPixel>.Black, source.Bounds.Width * .5F, source.Bounds); return Glow(source, GraphicsOptions.Default);
} }
/// <summary> /// <summary>
@ -38,7 +38,7 @@ namespace ImageSharp
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color) public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return Glow(source, color, source.Bounds.Width * .5F, source.Bounds); return Glow(source, color, GraphicsOptions.Default);
} }
/// <summary> /// <summary>
@ -51,7 +51,7 @@ namespace ImageSharp
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, float radius) public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, float radius)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return Glow(source, NamedColors<TPixel>.Black, radius, source.Bounds); return Glow(source, radius, GraphicsOptions.Default);
} }
/// <summary> /// <summary>
@ -66,7 +66,7 @@ namespace ImageSharp
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, Rectangle rectangle) public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return Glow(source, NamedColors<TPixel>.Black, 0, rectangle); return Glow(source, rectangle, GraphicsOptions.Default);
} }
/// <summary> /// <summary>
@ -83,7 +83,82 @@ namespace ImageSharp
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color, float radius, Rectangle rectangle) public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color, float radius, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
GlowProcessor<TPixel> processor = new GlowProcessor<TPixel>(color) { Radius = radius, }; return Glow(source, color, radius, rectangle, GraphicsOptions.Default);
}
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return Glow(source, NamedColors<TPixel>.Black, source.Bounds.Width * .5F, source.Bounds, options);
}
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the glow.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return Glow(source, color, source.Bounds.Width * .5F, source.Bounds, options);
}
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The the radius.</param>
/// <param name="options">The options effecting things like blending.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, float radius, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return Glow(source, NamedColors<TPixel>.Black, radius, source.Bounds, options);
}
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="options">The options effecting things like blending.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, Rectangle rectangle, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return Glow(source, NamedColors<TPixel>.Black, 0, rectangle, options);
}
/// <summary>
/// Applies a radial glow effect to an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the glow.</param>
/// <param name="radius">The the radius.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="options">The options effecting things like blending.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Glow<TPixel>(this Image<TPixel> source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
GlowProcessor<TPixel> processor = new GlowProcessor<TPixel>(color, options) { Radius = radius, };
source.ApplyProcessor(processor, rectangle); source.ApplyProcessor(processor, rectangle);
return source; return source;
} }

2
src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs

@ -42,7 +42,7 @@ namespace ImageSharp.Processing.Processors
protected override void AfterApply(ImageBase<TPixel> source, Rectangle sourceRectangle) protected override void AfterApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{ {
new VignetteProcessor<TPixel>(veryDarkOrange).Apply(source, sourceRectangle); new VignetteProcessor<TPixel>(veryDarkOrange).Apply(source, sourceRectangle);
new GlowProcessorParallel<TPixel>(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); new GlowProcessor<TPixel>(lightOrange, GraphicsOptions.Default) { Radius = source.Width / 4F }.Apply(source, sourceRectangle);
} }
} }
} }

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

@ -18,13 +18,19 @@ namespace ImageSharp.Processing.Processors
internal class GlowProcessor<TPixel> : ImageProcessor<TPixel> internal class GlowProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly GraphicsOptions options;
private readonly PixelBlender<TPixel> blender;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor{TPixel}" /> class. /// Initializes a new instance of the <see cref="GlowProcessor{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="color">The color or the glow.</param> /// <param name="color">The color or the glow.</param>
public GlowProcessor(TPixel color) /// <param name="options">The options effecting blending and composition.</param>
public GlowProcessor(TPixel color, GraphicsOptions options)
{ {
this.options = options;
this.GlowColor = color; this.GlowColor = color;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(this.options.BlenderMode);
} }
/// <summary> /// <summary>
@ -67,7 +73,6 @@ namespace ImageSharp.Processing.Processors
int width = maxX - minX; int width = maxX - minX;
using (Buffer<TPixel> rowColors = new Buffer<TPixel>(width)) using (Buffer<TPixel> rowColors = new Buffer<TPixel>(width))
using (Buffer<float> amounts = new Buffer<float>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{ {
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
@ -75,22 +80,27 @@ namespace ImageSharp.Processing.Processors
rowColors[i] = glowColor; rowColors[i] = glowColor;
} }
// TODO move GraphicOptions into core so all processes can use it. Parallel.For(
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Normal); minY,
for (int y = minY; y < maxY; y++) maxY,
{ this.ParallelOptions,
int offsetY = y - startY; y =>
int offsetX = minX - startX; {
for (int i = 0; i < width; i++) using (Buffer<float> amounts = new Buffer<float>(width))
{ {
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); int offsetY = y - startY;
amounts[i] = (1 - (.95F * (distance / maxDistance))).Clamp(0, 1); int offsetX = minX - startX;
} for (int i = 0; i < width; i++)
{
float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1);
}
BufferSpan<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); BufferSpan<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width);
blender.Compose(destination, destination, rowColors, amounts); this.blender.Blend(destination, destination, rowColors, amounts);
} }
});
} }
} }
} }

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

@ -1,98 +0,0 @@
// <copyright file="GlowProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Processing.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
using ImageSharp.PixelFormats;
/// <summary>
/// An <see cref="IImageProcessor{TPixel}"/> that applies a radial glow effect an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GlowProcessorParallel<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessorParallel{TPixel}" /> class.
/// </summary>
/// <param name="color">The color or the glow.</param>
public GlowProcessorParallel(TPixel color)
{
this.GlowColor = color;
}
/// <summary>
/// Gets or sets the glow color to apply.
/// </summary>
public TPixel GlowColor { get; set; }
/// <summary>
/// Gets or sets the the radius.
/// </summary>
public float Radius { get; set; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
TPixel glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2();
float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
int width = maxX - minX;
using (Buffer<TPixel> rowColors = new Buffer<TPixel>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
for (int i = 0; i < width; i++)
{
rowColors[i] = glowColor;
}
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TPixel packed = default(TPixel);
packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance))));
sourcePixels[offsetX, offsetY] = packed;
}
});
}
}
}
}

87
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -12,6 +12,10 @@ namespace ImageSharp.Benchmarks
using ImageSharp.Processing.Processors; using ImageSharp.Processing.Processors;
using CoreImage = ImageSharp.Image; using CoreImage = ImageSharp.Image;
using CoreSize = ImageSharp.Size; using CoreSize = ImageSharp.Size;
using ImageSharp.Processing;
using System.Numerics;
using System;
using System.Threading.Tasks;
public class Glow : BenchmarkBase public class Glow : BenchmarkBase
{ {
@ -21,7 +25,7 @@ namespace ImageSharp.Benchmarks
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.bulk = new GlowProcessor<Rgba32>(NamedColors<Rgba32>.Beige) { Radius = 800 * .5f, }; this.bulk = new GlowProcessor<Rgba32>(NamedColors<Rgba32>.Beige, GraphicsOptions.Default) { Radius = 800 * .5f, };
this.parallel = new GlowProcessorParallel<Rgba32>(NamedColors<Rgba32>.Beige) { Radius = 800 * .5f, }; this.parallel = new GlowProcessorParallel<Rgba32>(NamedColors<Rgba32>.Beige) { Radius = 800 * .5f, };
} }
@ -44,5 +48,86 @@ namespace ImageSharp.Benchmarks
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);
} }
} }
internal class GlowProcessorParallel<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessorParallel{TPixel}" /> class.
/// </summary>
/// <param name="color">The color or the glow.</param>
public GlowProcessorParallel(TPixel color)
{
this.GlowColor = color;
}
/// <summary>
/// Gets or sets the glow color to apply.
/// </summary>
public TPixel GlowColor { get; set; }
/// <summary>
/// Gets or sets the the radius.
/// </summary>
public float Radius { get; set; }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
TPixel glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2();
float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
int width = maxX - minX;
using (Buffer<TPixel> rowColors = new Buffer<TPixel>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
for (int i = 0; i < width; i++)
{
rowColors[i] = glowColor;
}
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TPixel packed = default(TPixel);
packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance))));
sourcePixels[offsetX, offsetY] = packed;
}
});
}
}
}
} }
} }

56
tests/ImageSharp.Tests/Drawing/BlendedShapes.cs

@ -0,0 +1,56 @@
// <copyright file="DrawImageEffectTest.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests.Drawing
{
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using ImageSharp.PixelFormats;
using Xunit;
public class BlendedShapes
{
public static IEnumerable<object[]> modes = ((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode)))
.Select(x=> new object[] { x });
[Theory]
[WithBlankImages(nameof(modes), 100, 100, PixelTypes.StandardImageClass)]
public void DrawBlendedValues<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel>
{
using (var img = provider.GetImage())
{
img.Fill(NamedColors<TPixel>.DarkBlue, new Rectangle(0, 40, 100, 20));
img.Fill(NamedColors<TPixel>.HotPink, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true)
{
BlenderMode = mode
});
img.DebugSave(provider, new { mode });
}
}
[Theory]
[WithBlankImages(nameof(modes), 100, 100, PixelTypes.StandardImageClass)]
public void DrawBlendedValues_transparent<TPixel>(TestImageProvider<TPixel> provider, PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel>
{
using (var img = provider.GetImage())
{
img.Fill(NamedColors<TPixel>.DarkBlue, new Rectangle(0, 40, 100, 20));
img.Fill(NamedColors<TPixel>.HotPink, new Rectangle(20, 0, 40, 100), new ImageSharp.GraphicsOptions(true)
{
BlenderMode = mode
});
img.Fill(NamedColors<TPixel>.Transparent, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true)
{
BlenderMode = mode
});
img.DebugSave(provider, new { mode });
}
}
}
}

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

@ -1,29 +1,29 @@
// <copyright file="DrawImageEffectTest.cs" company="James Jackson-South"> // <copyright file="DrawImageEffectTest.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Tests namespace ImageSharp.Tests
{ {
using System.IO; using System.IO;
using ImageSharp.Drawing; using ImageSharp.Drawing;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using Xunit; using Xunit;
public class DrawImageEffectTest : FileTestBase public class DrawImageEffectTest : FileTestBase
{ {
[Fact] [Fact]
public void ImageShouldApplyDrawImageFilter() public void ImageShouldApplyDrawImageFilter()
{ {
string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect"); string path = this.CreateOutputDirectory("Drawing", "DrawImageEffect");
PixelBlenderMode[] modes = (PixelBlenderMode[])System.Enum.GetValues(typeof(PixelBlenderMode)); PixelBlenderMode[] modes = (PixelBlenderMode[])System.Enum.GetValues(typeof(PixelBlenderMode));
using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage()) using (Image blend = TestFile.Create(TestImages.Png.Blur).CreateImage())
{ {
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
using (Image image = file.CreateImage()) using (Image image = file.CreateImage())
{ {
foreach (PixelBlenderMode mode in modes) foreach (PixelBlenderMode mode in modes)
{ {
@ -37,10 +37,10 @@ namespace ImageSharp.Tests
BlendPercentage = .75f BlendPercentage = .75f
}).Save(output); }).Save(output);
} }
} }
} }
} }
} }
} }
} }
} }

36
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs

@ -41,7 +41,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void NormalBlendFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void NormalBlendFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultNormalPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultNormalPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -51,7 +51,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultNormalPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultNormalPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -80,7 +80,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void MultiplyFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void MultiplyFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultMultiplyPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultMultiplyPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -90,7 +90,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultMultiplyPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultMultiplyPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -119,7 +119,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void AddFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void AddFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultAddPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultAddPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -129,7 +129,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultAddPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultAddPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -158,7 +158,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void SubstractFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void SubstractFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultSubstractPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultSubstractPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -168,7 +168,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultSubstractPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultSubstractPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -197,7 +197,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void ScreenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void ScreenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultScreenPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultScreenPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -207,7 +207,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultScreenPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultScreenPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -236,7 +236,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void DarkenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void DarkenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultDarkenPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultDarkenPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -246,7 +246,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultDarkenPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultDarkenPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -275,7 +275,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void LightenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void LightenFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultLightenPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultLightenPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -285,7 +285,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultLightenPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultLightenPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -314,7 +314,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void OverlayFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void OverlayFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultOverlayPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultOverlayPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -324,7 +324,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultOverlayPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultOverlayPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
@ -353,7 +353,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
public void HardLightFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected) public void HardLightFunction_Blender<TPixel>(TestPixel<TPixel> back, TestPixel<TPixel> source, float amount, TestPixel<TPixel> expected)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
TPixel actual = new DefaultHardLightPixelBlender<TPixel>().Compose(back, source, amount); TPixel actual = new DefaultHardLightPixelBlender<TPixel>().Blend(back, source, amount);
VectorAssert.Equal(expected, actual, 2); VectorAssert.Equal(expected, actual, 2);
} }
@ -363,7 +363,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]); BufferSpan<TPixel> dest = new BufferSpan<TPixel>(new TPixel[1]);
new DefaultHardLightPixelBlender<TPixel>().Compose(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); new DefaultHardLightPixelBlender<TPixel>().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount));
VectorAssert.Equal(expected, dest[0], 2); VectorAssert.Equal(expected, dest[0], 2);
} }
} }

Loading…
Cancel
Save