Browse Source

initial blender implementations

pull/202/head
Scott Williams 9 years ago
parent
commit
472bb85030
  1. 3
      src/ImageSharp.Drawing/Brushes/IBrush.cs
  2. 66
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  3. 43
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  4. 53
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  5. 55
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  6. 83
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  7. 16
      src/ImageSharp.Drawing/FillRegion.cs
  8. 56
      src/ImageSharp.Drawing/GraphicsOptions.cs
  9. 3
      src/ImageSharp.Drawing/Pens/IPen.cs
  10. 15
      src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs
  11. 2
      src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
  12. 7
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  13. 133
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  14. 4
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  15. 2
      src/ImageSharp/Image/PixelAccessor{TPixel}.cs
  16. 2
      src/ImageSharp/PixelFormats/Alpha8.cs
  17. 2
      src/ImageSharp/PixelFormats/Argb32.cs
  18. 2
      src/ImageSharp/PixelFormats/Bgr565.cs
  19. 2
      src/ImageSharp/PixelFormats/Bgra4444.cs
  20. 2
      src/ImageSharp/PixelFormats/Bgra5551.cs
  21. 2
      src/ImageSharp/PixelFormats/Byte4.cs
  22. 2
      src/ImageSharp/PixelFormats/HalfSingle.cs
  23. 2
      src/ImageSharp/PixelFormats/HalfVector2.cs
  24. 2
      src/ImageSharp/PixelFormats/HalfVector4.cs
  25. 8
      src/ImageSharp/PixelFormats/IPixel.cs
  26. 2
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  27. 2
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  28. 2
      src/ImageSharp/PixelFormats/NormalizedShort2.cs
  29. 2
      src/ImageSharp/PixelFormats/NormalizedShort4.cs
  30. 83
      src/ImageSharp/PixelFormats/PixelBlenderMode.cs
  31. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs
  32. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs
  33. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs
  34. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs
  35. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs
  36. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs
  37. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs
  38. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs
  39. 37
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs
  40. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs
  41. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs
  42. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs
  43. 42
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs
  44. 39
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  45. 125
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  46. 20
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  47. 2
      src/ImageSharp/PixelFormats/Rg32.cs
  48. 2
      src/ImageSharp/PixelFormats/Rgba1010102.cs
  49. 6
      src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
  50. 2
      src/ImageSharp/PixelFormats/Rgba32.cs
  51. 2
      src/ImageSharp/PixelFormats/Rgba64.cs
  52. 4
      src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs
  53. 2
      src/ImageSharp/PixelFormats/RgbaVector.cs
  54. 2
      src/ImageSharp/PixelFormats/Short2.cs
  55. 2
      src/ImageSharp/PixelFormats/Short4.cs
  56. 2
      src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs
  57. 2
      src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs
  58. 40
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  59. 100
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs
  60. 2
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  61. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
  62. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  63. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
  64. 4
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
  65. 48
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  66. 12
      tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs
  67. 6
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  68. 53
      tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs
  69. 2
      tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs
  70. 62
      tests/ImageSharp.Tests/TestUtilities/TestPixel.cs

3
src/ImageSharp.Drawing/Brushes/IBrush.cs

@ -24,6 +24,7 @@ namespace ImageSharp.Drawing
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
/// <param name="region">The region the brush will be applied to.</param>
/// <param name="options">The graphic options</param>
/// <returns>
/// The brush applicator for this brush
/// </returns>
@ -31,6 +32,6 @@ namespace ImageSharp.Drawing
/// The <paramref name="region" /> when being applied to things like shapes would usually be the
/// bounding box of the shape not necessarily the bounds of the whole image
/// </remarks>
BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> pixelSource, RectangleF region);
BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> pixelSource, RectangleF region, GraphicsOptions options);
}
}

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

@ -31,9 +31,9 @@ namespace ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region)
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
{
return new ImageBrushApplicator(sourcePixels, this.image, region);
return new ImageBrushApplicator(sourcePixels, this.image, region, options);
}
/// <summary>
@ -57,9 +57,14 @@ namespace ImageSharp.Drawing.Brushes
private readonly int xLength;
/// <summary>
/// The offset.
/// The Y offset.
/// </summary>
private readonly Vector2 offset;
private readonly int offsetY;
/// <summary>
/// The X offset.
/// </summary>
private readonly int offsetX;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator"/> class.
@ -70,16 +75,18 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="region">
/// The region.
/// </param>
/// <param name="options">The options</param>
/// <param name="sourcePixels">
/// The sourcePixels.
/// </param>
public ImageBrushApplicator(PixelAccessor<TPixel> sourcePixels, IImageBase<TPixel> image, RectangleF region)
: base(sourcePixels)
public ImageBrushApplicator(PixelAccessor<TPixel> sourcePixels, IImageBase<TPixel> image, RectangleF region, GraphicsOptions options)
: base(sourcePixels, options)
{
this.source = image.Lock();
this.xLength = image.Width;
this.yLength = image.Height;
this.offset = new Vector2(MathF.Max(MathF.Floor(region.Top), 0), MathF.Max(MathF.Floor(region.Left), 0));
this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0);
this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0);
}
/// <summary>
@ -94,13 +101,8 @@ namespace ImageSharp.Drawing.Brushes
{
get
{
Vector2 point = new Vector2(x, y);
// Offset the requested pixel by the value in the rectangle (the shapes position)
point = point - this.offset;
int srcX = (int)point.X % this.xLength;
int srcY = (int)point.Y % this.yLength;
int srcX = (x - this.offsetX) % this.xLength;
int srcY = (y - this.offsetY) % this.yLength;
return this.source[srcX, srcY];
}
}
@ -112,33 +114,27 @@ namespace ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
internal override void Apply(BufferSpan<float> scanline, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
// create a span for colors
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
{
BufferSpan<float> slice = buffer.Slice(offset);
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
BufferSpan<TPixel> sourceRow = this.source.GetRowSpan(sourceY);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
for (int i = 0; i < scanline.Length; i++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = this[targetX, targetY].ToVector4();
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TPixel packed = default(TPixel);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX];
overlay[i] = pixel;
}
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}

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

@ -90,9 +90,9 @@ namespace ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region)
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
{
return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector);
return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options);
}
/// <summary>
@ -112,8 +112,9 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="sourcePixels">The sourcePixels.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="patternVector">The patternVector.</param>
public PatternBrushApplicator(PixelAccessor<TPixel> sourcePixels, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector)
: base(sourcePixels)
/// <param name="options">The options</param>
public PatternBrushApplicator(PixelAccessor<TPixel> sourcePixels, Fast2DArray<TPixel> pattern, Fast2DArray<Vector4> patternVector, GraphicsOptions options)
: base(sourcePixels, options)
{
this.pattern = pattern;
this.patternVector = patternVector;
@ -146,34 +147,22 @@ namespace ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
internal override void Apply(BufferSpan<float> scanline, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
int patternY = y % this.pattern.Height;
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
for (int i = 0; i < scanline.Length; i++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
// 2d array index at row/column
Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width];
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TPixel packed = default(TPixel);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
int patternX = (x + i) % this.pattern.Width;
overlay[i] = this.pattern[y, x];
}
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}

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

@ -21,16 +21,31 @@ namespace ImageSharp.Drawing.Processors
/// Initializes a new instance of the <see cref="BrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="target">The target.</param>
internal BrushApplicator(PixelAccessor<TPixel> target)
/// <param name="options">The options.</param>
internal BrushApplicator(PixelAccessor<TPixel> target, GraphicsOptions options)
{
this.Target = target;
this.Options = options;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options.BlenderMode);
}
/// <summary>
/// Gets the blendder
/// </summary>
internal PixelBlender<TPixel> Blender { get; }
/// <summary>
/// Gets the destinaion
/// </summary>
protected PixelAccessor<TPixel> Target { get; }
/// <summary>
/// Gets the blend percentage
/// </summary>
protected GraphicsOptions Options { get; private set; }
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
@ -45,39 +60,27 @@ namespace ImageSharp.Drawing.Processors
/// <summary>
/// Applies the opactiy weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
/// </summary>
/// <param name="scanlineBuffer">The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.</param>
/// <param name="scanlineWidth">The number of pixels effected by this scanline.</param>
/// <param name="offset">The offset fromthe begining of <paramref name="scanlineBuffer" /> the opacity data starts.</param>
/// <param name="scanline">The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.</param>
/// <param name="x">The x position in the target pixel space that the start of the scanline data corresponds to.</param>
/// <param name="y">The y position in the target pixel space that whole scanline corresponds to.</param>
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
internal virtual void Apply(BufferSpan<float> scanline, int x, int y)
{
DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
for (int i = 0; i < scanline.Length; i++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
if (this.Options.BlendPercentage < 1)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = this[targetX, targetY].ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TPixel packed = default(TPixel);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
}
overlay[i] = this[x + i, y];
}
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}

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

@ -54,9 +54,9 @@ namespace ImageSharp.Drawing.Brushes
public TPixel TargeTPixel { get; }
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region)
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
{
return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold);
return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold, options);
}
/// <summary>
@ -86,8 +86,9 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="sourceColor">Color of the source.</param>
/// <param name="targeTPixel">Color of the target.</param>
/// <param name="threshold">The threshold .</param>
public RecolorBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold)
: base(sourcePixels)
/// <param name="options">The options</param>
public RecolorBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold, GraphicsOptions options)
: base(sourcePixels, options)
{
this.sourceColor = sourceColor.ToVector4();
this.targeTPixel = targeTPixel.ToVector4();
@ -136,42 +137,24 @@ namespace ImageSharp.Drawing.Brushes
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
internal override void Apply(BufferSpan<float> scanline, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
using (Buffer<TPixel> overlay = new Buffer<TPixel>(scanline.Length))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
for (int i = 0; i < scanline.Length; i++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = backgroundVector;
float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor);
if (distance <= this.threshold)
{
float lerpAmount = (this.threshold - distance) / this.threshold;
sourceVector = Vector4BlendTransforms.PremultipliedLerp(
sourceVector,
this.targeTPixel,
lerpAmount);
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TPixel packed = default(TPixel);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
}
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
int offsetX = x + i;
// no doubt this one can be optermised further but I can't imagine its
// actually being used and can probably be removed/interalised for now
overlay[i] = this[offsetX, y];
}
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer);
}
}
}

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

@ -39,34 +39,61 @@ namespace ImageSharp.Drawing.Brushes
public TPixel Color => this.color;
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region)
public BrushApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
{
return new SolidBrushApplicator(sourcePixels, this.color);
if (options.BlendPercentage < 0)
{
return new SolidBrushApplicator(sourcePixels, this.color, options);
}
else
{
return new SolidNoBlendBrushApplicator(sourcePixels, this.color, options);
}
}
/// <summary>
/// The solid brush applicator.
/// </summary>
private class SolidBrushApplicator : BrushApplicator<TPixel>
private class SolidNoBlendBrushApplicator : SolidBrushApplicator
{
/// <summary>
/// The solid color.
/// </summary>
private readonly TPixel color;
private readonly Vector4 colorVector;
public SolidNoBlendBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel color, GraphicsOptions options)
: base(sourcePixels, color, options)
{
}
internal override void Apply(BufferSpan<float> scanline, int x, int y)
{
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
this.Blender.Compose(destinationRow, destinationRow, this.Colors, scanline);
}
}
/// <summary>
/// The solid brush applicator.
/// </summary>
private class SolidBrushApplicator : BrushApplicator<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="options">The options</param>
/// <param name="sourcePixels">The sourcePixels.</param>
public SolidBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel color)
: base(sourcePixels)
public SolidBrushApplicator(PixelAccessor<TPixel> sourcePixels, TPixel color, GraphicsOptions options)
: base(sourcePixels, options)
{
this.color = color;
this.colorVector = color.ToVector4();
this.Colors = new Buffer<TPixel>(sourcePixels.Width);
for (int i = 0; i < this.Colors.Length; i++)
{
this.Colors[i] = color;
}
}
/// <summary>
/// Gets the colors.
/// </summary>
protected Buffer<TPixel> Colors { get; }
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
@ -75,41 +102,27 @@ namespace ImageSharp.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] => this.color;
internal override TPixel this[int x, int y] => this.Colors[x];
/// <inheritdoc />
public override void Dispose()
{
// noop
this.Colors.Dispose();
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
internal override void Apply(BufferSpan<float> scanline, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
BufferSpan<TPixel> destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length);
using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
using (Buffer<float> amountBuffer = new Buffer<float>(scanline.Length))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
for (int i = 0; i < scanline.Length; i++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = this.colorVector;
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TPixel packed = default(TPixel);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
}
this.Blender.Compose(destinationRow, destinationRow, this.Colors, amountBuffer);
}
}
}

16
src/ImageSharp.Drawing/FillRegion.cs

@ -15,6 +15,20 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Flood fills the image with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The details how to fill the region of interest.</param>
/// <param name="options">The graphics options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Apply(new FillProcessor<TPixel>(brush, options));
}
/// <summary>
/// Flood fills the image with the specified brush.
/// </summary>
@ -25,7 +39,7 @@ namespace ImageSharp
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush)
where TPixel : struct, IPixel<TPixel>
{
return source.Apply(new FillProcessor<TPixel>(brush));
return source.Fill(brush, GraphicsOptions.Default);
}
/// <summary>

56
src/ImageSharp.Drawing/GraphicsOptions.cs

@ -5,6 +5,8 @@
namespace ImageSharp.Drawing
{
using ImageSharp.PixelFormats;
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
@ -15,24 +17,60 @@ namespace ImageSharp.Drawing
/// </summary>
public static readonly GraphicsOptions Default = new GraphicsOptions(true);
private float? blendPercentage;
private int? antialiasSubpixelDepth;
private bool? antialias;
private PixelBlenderMode blenderMode;
/// <summary>
/// Whether antialiasing should be applied.
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
public bool Antialias;
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing)
{
this.blenderMode = PixelBlenderMode.Default;
this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// The number of subpixels to use while rendering with antialiasing enabled.
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public int AntialiasSubpixelDepth;
public bool Antialias
{
get => this.antialias ?? true;
set => this.antialias = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing)
public int AntialiasSubpixelDepth
{
get => this.antialiasSubpixelDepth ?? 16;
set => this.antialiasSubpixelDepth = value;
}
/// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary>
public float BlendPercentage
{
get => this.blendPercentage ?? 1;
set => this.blendPercentage = value;
}
/// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary>
public PixelBlenderMode BlenderMode
{
this.Antialias = enableAntialiasing;
this.AntialiasSubpixelDepth = 16;
get => this.blenderMode;
set => this.blenderMode = value;
}
}
}

3
src/ImageSharp.Drawing/Pens/IPen.cs

@ -20,12 +20,13 @@ namespace ImageSharp.Drawing.Pens
/// </summary>
/// <param name="pixelSource">The pixel source.</param>
/// <param name="region">The region the pen will be applied to.</param>
/// <param name="options">The currently active graphic options.</param>
/// <returns>
/// Returns a the applicator for the pen.
/// </returns>
/// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would usually be the bounding box of the shape not necessarily the shape of the whole image.
/// </remarks>
PenApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> pixelSource, RectangleF region);
PenApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> pixelSource, RectangleF region, GraphicsOptions options);
}
}

15
src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs

@ -103,6 +103,7 @@ namespace ImageSharp.Drawing.Pens
/// </summary>
/// <param name="sourcePixels">The source pixels.</param>
/// <param name="region">The region the pen will be applied to.</param>
/// <param name="options">The Graphics options</param>
/// <returns>
/// Returns a the applicator for the pen.
/// </returns>
@ -110,16 +111,16 @@ namespace ImageSharp.Drawing.Pens
/// The <paramref name="region" /> when being applied to things like shapes would ussually be the
/// bounding box of the shape not necorserrally the shape of the whole image
/// </remarks>
public PenApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region)
public PenApplicator<TPixel> CreateApplicator(PixelAccessor<TPixel> sourcePixels, RectangleF region, GraphicsOptions options)
{
if (this.pattern == null || this.pattern.Length < 2)
{
// if there is only one item in the pattern then 100% of it will
// be solid so use the quicker applicator
return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width);
return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width, options);
}
return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern);
return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern, options);
}
private class SolidPenApplicator : PenApplicator<TPixel>
@ -127,9 +128,9 @@ namespace ImageSharp.Drawing.Pens
private readonly BrushApplicator<TPixel> brush;
private readonly float halfWidth;
public SolidPenApplicator(PixelAccessor<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width)
public SolidPenApplicator(PixelAccessor<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, GraphicsOptions options)
{
this.brush = brush.CreateApplicator(sourcePixels, region);
this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.halfWidth = width / 2;
this.RequiredRegion = RectangleF.Outset(region, width);
}
@ -170,9 +171,9 @@ namespace ImageSharp.Drawing.Pens
private readonly float[] pattern;
private readonly float totalLength;
public PatternPenApplicator(PixelAccessor<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, float[] pattern)
public PatternPenApplicator(PixelAccessor<TPixel> sourcePixels, IBrush<TPixel> brush, RectangleF region, float width, float[] pattern, GraphicsOptions options)
{
this.brush = brush.CreateApplicator(sourcePixels, region);
this.brush = brush.CreateApplicator(sourcePixels, region, options);
this.halfWidth = width / 2;
this.totalLength = 0;

2
src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs

@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors
protected override void OnApply(ImageBase<TPixel> source, Rectangle sourceRectangle)
{
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds))
using (PenApplicator<TPixel> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options))
{
Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion);

7
src/ImageSharp.Drawing/Processors/FillProcessor.cs

@ -24,14 +24,17 @@ namespace ImageSharp.Drawing.Processors
/// The brush.
/// </summary>
private readonly IBrush<TPixel> brush;
private readonly GraphicsOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="FillProcessor{TPixel}"/> class.
/// </summary>
/// <param name="brush">The brush to source pixel colors from.</param>
public FillProcessor(IBrush<TPixel> brush)
/// <param name="options">The options</param>
public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options)
{
this.brush = brush;
this.options = options;
}
/// <inheritdoc/>
@ -63,7 +66,7 @@ namespace ImageSharp.Drawing.Processors
// for example If brush is SolidBrush<TPixel> then we could just get the color upfront
// and skip using the IBrushApplicator<TPixel>?.
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options))
{
Parallel.For(
minY,

133
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -88,104 +88,105 @@ namespace ImageSharp.Drawing.Processors
}
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(sourcePixels, rect))
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options))
{
float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX;
float[] scanline = ArrayPool<float>.Shared.Rent(scanlineWidth);
try
using (Buffer<float> scanline = new Buffer<float>(scanlineWidth))
{
bool scanlineDirty = true;
for (int y = minY; y < maxY; y++)
try
{
if (scanlineDirty)
bool scanlineDirty = true;
for (int y = minY; y < maxY; y++)
{
// clear the buffer
for (int x = 0; x < scanlineWidth; x++)
if (scanlineDirty)
{
scanline[x] = 0;
}
scanlineDirty = false;
}
// clear the buffer
for (int x = 0; x < scanlineWidth; x++)
{
scanline[x] = 0;
}
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
scanlineDirty = false;
}
QuickSort(buffer, pointsFound);
for (int point = 0; point < pointsFound; point += 2)
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart);
int endX = (int)MathF.Floor(scanEnd);
int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
if (pointsFound == 0)
{
// nothing on this line skip
continue;
}
if (startX >= 0 && startX < scanline.Length)
QuickSort(buffer, pointsFound);
for (int point = 0; point < pointsFound; point += 2)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart);
int endX = (int)MathF.Floor(scanEnd);
if (startX >= 0 && startX < scanline.Length)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
}
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
if (endX >= 0 && endX < scanline.Length)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
}
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
if (nextX >= 0)
{
for (int x = nextX; x < endX; x++)
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
if (nextX >= 0)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
}
}
if (scanlineDirty)
{
if (!this.Options.Antialias)
if (scanlineDirty)
{
for (int x = 0; x < scanlineWidth; x++)
if (!this.Options.Antialias)
{
if (scanline[x] > 0.5)
{
scanline[x] = 1;
}
else
for (int x = 0; x < scanlineWidth; x++)
{
scanline[x] = 0;
if (scanline[x] > 0.5)
{
scanline[x] = 1;
}
else
{
scanline[x] = 0;
}
}
}
}
applicator.Apply(scanline, scanlineWidth, 0, minX, y);
applicator.Apply(scanline, minX, y);
}
}
}
}
finally
{
arrayPool.Return(buffer);
ArrayPool<float>.Shared.Return(scanline);
finally
{
arrayPool.Return(buffer);
}
}
}
}

4
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -623,13 +623,13 @@ namespace ImageSharp.Formats
case PngColorType.Rgb:
BulkPixelOperations<TPixel>.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width);
PixelOperations<TPixel>.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width);
break;
case PngColorType.RgbWithAlpha:
BulkPixelOperations<TPixel>.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width);
PixelOperations<TPixel>.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width);
break;
}

2
src/ImageSharp/Image/PixelAccessor{TPixel}.cs

@ -116,7 +116,7 @@ namespace ImageSharp
/// <inheritdoc />
BufferSpan<TPixel> IBuffer2D<TPixel>.Span => this.pixelBuffer;
private static BulkPixelOperations<TPixel> Operations => BulkPixelOperations<TPixel>.Instance;
private static PixelOperations<TPixel> Operations => PixelOperations<TPixel>.Instance;
/// <summary>
/// Gets or sets the pixel at the specified position.

2
src/ImageSharp/PixelFormats/Alpha8.cs

@ -60,7 +60,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Alpha8> CreateBulkOperations() => new BulkPixelOperations<Alpha8>();
public PixelOperations<Alpha8> CreateBulkOperations() => new PixelOperations<Alpha8>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Argb32.cs

@ -221,7 +221,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Argb32> CreateBulkOperations() => new BulkPixelOperations<Argb32>();
public PixelOperations<Argb32> CreateBulkOperations() => new PixelOperations<Argb32>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Bgr565.cs

@ -69,7 +69,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Bgr565> CreateBulkOperations() => new BulkPixelOperations<Bgr565>();
public PixelOperations<Bgr565> CreateBulkOperations() => new PixelOperations<Bgr565>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector3"/>.

2
src/ImageSharp/PixelFormats/Bgra4444.cs

@ -68,7 +68,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Bgra4444> CreateBulkOperations() => new BulkPixelOperations<Bgra4444>();
public PixelOperations<Bgra4444> CreateBulkOperations() => new PixelOperations<Bgra4444>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -69,7 +69,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Bgra5551> CreateBulkOperations() => new BulkPixelOperations<Bgra5551>();
public PixelOperations<Bgra5551> CreateBulkOperations() => new PixelOperations<Bgra5551>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Byte4.cs

@ -71,7 +71,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Byte4> CreateBulkOperations() => new BulkPixelOperations<Byte4>();
public PixelOperations<Byte4> CreateBulkOperations() => new PixelOperations<Byte4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/HalfSingle.cs

@ -73,7 +73,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<HalfSingle> CreateBulkOperations() => new BulkPixelOperations<HalfSingle>();
public PixelOperations<HalfSingle> CreateBulkOperations() => new PixelOperations<HalfSingle>();
/// <summary>
/// Expands the packed representation into a <see cref="float"/>.

2
src/ImageSharp/PixelFormats/HalfVector2.cs

@ -83,7 +83,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<HalfVector2> CreateBulkOperations() => new BulkPixelOperations<HalfVector2>();
public PixelOperations<HalfVector2> CreateBulkOperations() => new PixelOperations<HalfVector2>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.

2
src/ImageSharp/PixelFormats/HalfVector4.cs

@ -86,7 +86,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<HalfVector4> CreateBulkOperations() => new BulkPixelOperations<HalfVector4>();
public PixelOperations<HalfVector4> CreateBulkOperations() => new PixelOperations<HalfVector4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

8
src/ImageSharp/PixelFormats/IPixel.cs

@ -16,11 +16,11 @@ namespace ImageSharp.PixelFormats
where TSelf : struct, IPixel<TSelf>
{
/// <summary>
/// Creates a <see cref="BulkPixelOperations{TPixel}"/> instance for this pixel type.
/// This method is not intended to be consumed directly. Use <see cref="BulkPixelOperations{TPixel}.Instance"/> instead.
/// Creates a <see cref="PixelOperations{TPixel}"/> instance for this pixel type.
/// This method is not intended to be consumed directly. Use <see cref="PixelOperations{TPixel}.Instance"/> instead.
/// </summary>
/// <returns>The <see cref="BulkPixelOperations{TPixel}"/> instance.</returns>
BulkPixelOperations<TSelf> CreateBulkOperations();
/// <returns>The <see cref="PixelOperations{TPixel}"/> instance.</returns>
PixelOperations<TSelf> CreateBulkOperations();
}
/// <summary>

2
src/ImageSharp/PixelFormats/NormalizedByte2.cs

@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<NormalizedByte2> CreateBulkOperations() => new BulkPixelOperations<NormalizedByte2>();
public PixelOperations<NormalizedByte2> CreateBulkOperations() => new PixelOperations<NormalizedByte2>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.

2
src/ImageSharp/PixelFormats/NormalizedByte4.cs

@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<NormalizedByte4> CreateBulkOperations() => new BulkPixelOperations<NormalizedByte4>();
public PixelOperations<NormalizedByte4> CreateBulkOperations() => new PixelOperations<NormalizedByte4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/NormalizedShort2.cs

@ -88,7 +88,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<NormalizedShort2> CreateBulkOperations() => new BulkPixelOperations<NormalizedShort2>();
public PixelOperations<NormalizedShort2> CreateBulkOperations() => new PixelOperations<NormalizedShort2>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/NormalizedShort4.cs

@ -90,7 +90,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<NormalizedShort4> CreateBulkOperations() => new BulkPixelOperations<NormalizedShort4>();
public PixelOperations<NormalizedShort4> CreateBulkOperations() => new PixelOperations<NormalizedShort4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

83
src/ImageSharp/PixelFormats/PixelBlenderMode.cs

@ -0,0 +1,83 @@
// <copyright file="PixelCompositor{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
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// The various blending modes.
/// </summary>
public enum PixelBlenderMode
{
/// <summary>
/// The default composition mode.
/// </summary>
/// <remarks> uses PremultipliedLerpTransform </remarks>
Default = 0,
/// <summary>
/// Normal transform.
/// </summary>
Normal,
/// <summary>
/// Multiply Transform.
/// </summary>
Multiply,
/// <summary>
/// Screen Transform.
/// </summary>
Screen,
/// <summary>
/// HardLight Transform.
/// </summary>
HardLight,
/// <summary>
/// Overlay Transform.
/// </summary>
Overlay,
/// <summary>
/// Darken Transform.
/// </summary>
Darken,
/// <summary>
/// Lighten Transform.
/// </summary>
Lighten,
/// <summary>
/// SoftLight Transform.
/// </summary>
SoftLight,
/// <summary>
/// Dodge Transform.
/// </summary>
Dodge,
/// <summary>
/// Burn Transform.
/// </summary>
Burn,
/// <summary>
/// Difference Transform.
/// </summary>
Difference,
/// <summary>
/// Exclusion Transform.
/// </summary>
Exclusion
}
}

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

@ -0,0 +1,42 @@
// <copyright file="DefaultBurnPixelBlender{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 DefaultBurnPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.Burn(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

@ -0,0 +1,42 @@
// <copyright file="DefaultDarkenPixelBlender{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 DefaultDarkenPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.Darken(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

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

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

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

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

@ -0,0 +1,42 @@
// <copyright file="DefaultHardLightPixelBlender{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 DefaultHardLightPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.HardLight(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

@ -0,0 +1,42 @@
// <copyright file="DefaultDarkenPixelBlender{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 DefaultLightenPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.Lighten(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

@ -0,0 +1,42 @@
// <copyright file="DefaultMultiplyPixelBlender{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 DefaultMultiplyPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.Multiply(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

@ -0,0 +1,37 @@
// <copyright file="DefaultNormalPixelBlender{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 ImageSharp.PixelFormats;
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal class DefaultNormalPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc />
public override TPixel Compose(TPixel background, TPixel source, float amount)
{
return source;
}
/// <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++)
{
destination[i] = source[i];
}
}
}
}

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

@ -0,0 +1,42 @@
// <copyright file="DefaultOverlayPixelBlender{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 DefaultOverlayPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.Overlay(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

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

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

@ -0,0 +1,42 @@
// <copyright file="DefaultScreenPixelBlender{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 DefaultScreenPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.Screen(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

@ -0,0 +1,42 @@
// <copyright file="DefaultSoftLightPixelBlender{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 DefaultSoftLightPixelBlender<TPixel> : PixelBlender<TPixel>
where TPixel : struct, IPixel<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;
}
/// <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.SoftLight(background[i].ToVector4(), source[i].ToVector4());
destination[i].PackFromVector4(result);
}
}
}
}

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

@ -0,0 +1,39 @@
// <copyright file="PixelCompositor{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
{
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal abstract class PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Composes 2 pixels together.
/// </summary>
/// <param name="background">The background color.</param>
/// <param name="source">The source color.</param>
/// <param name="amount">
/// 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.
/// </param>
/// <returns>The final pixel value after composition</returns>
public abstract TPixel Compose(TPixel background, TPixel source, float amount);
/// <summary>
/// Composes 2 pixels together.
/// </summary>
/// <param name="destination">The destination span.</param>
/// <param name="background">The background span.</param>
/// <param name="source">The source span.</param>
/// <param name="amount">
/// 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.
/// </param>
public abstract void Compose(BufferSpan<TPixel> destination, BufferSpan<TPixel> background, BufferSpan<TPixel> source, BufferSpan<float> amount);
}
}

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

@ -0,0 +1,125 @@
// <copyright file="PixelOperations{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
{
using System.Numerics;
using System.Runtime.CompilerServices;
using ImageSharp.PixelFormats.PixelBlenders;
#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag
/// <summary>
/// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
/// for pixel buffers of type <typeparamref name="TPixel"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public partial class PixelOperations<TPixel>
#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag
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>
/// 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>
/// <param name="mode">The blending mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelBlenderMode mode)
{
switch (mode)
{
case PixelBlenderMode.Normal:
return this.normalBlender;
case PixelBlenderMode.Multiply:
return this.multiplyBlender;
case PixelBlenderMode.Screen:
return this.screenBlender;
case PixelBlenderMode.HardLight:
return this.hardLightBlender;
case PixelBlenderMode.Overlay:
return this.overlayBlender;
case PixelBlenderMode.Darken:
return this.darkenBlender;
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;
default:
return this.premultipliedLerpBlender;
}
}
}
}

20
src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs → src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -1,4 +1,4 @@
// <copyright file="BulkPixelOperations{TPixel}.cs" company="James Jackson-South">
// <copyright file="PixelOperations{TPixel}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -8,18 +8,20 @@ namespace ImageSharp.PixelFormats
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
/// for pixel buffers of type <typeparamref name="TPixel"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class BulkPixelOperations<TPixel>
#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag
/// <summary>
/// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations
/// for pixel buffers of type <typeparamref name="TPixel"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public partial class PixelOperations<TPixel>
#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the global <see cref="BulkPixelOperations{TPixel}"/> instance for the pixel type <typeparamref name="TPixel"/>
/// Gets the global <see cref="PixelOperations{TPixel}"/> instance for the pixel type <typeparamref name="TPixel"/>
/// </summary>
public static BulkPixelOperations<TPixel> Instance { get; } = default(TPixel).CreateBulkOperations();
public static PixelOperations<TPixel> Instance { get; } = default(TPixel).CreateBulkOperations();
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromVector4(Vector4)"/>

2
src/ImageSharp/PixelFormats/Rg32.cs

@ -74,7 +74,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Rg32> CreateBulkOperations() => new BulkPixelOperations<Rg32>();
public PixelOperations<Rg32> CreateBulkOperations() => new PixelOperations<Rg32>();
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.

2
src/ImageSharp/PixelFormats/Rgba1010102.cs

@ -77,7 +77,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Rgba1010102> CreateBulkOperations() => new BulkPixelOperations<Rgba1010102>();
public PixelOperations<Rgba1010102> CreateBulkOperations() => new PixelOperations<Rgba1010102>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

6
src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs → src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs

@ -1,4 +1,4 @@
// <copyright file="Rgba32.BulkOperations.cs" company="James Jackson-South">
// <copyright file="Rgba32.PixelOperations.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -16,9 +16,9 @@ namespace ImageSharp.PixelFormats
public partial struct Rgba32
{
/// <summary>
/// <see cref="BulkPixelOperations{TPixel}"/> implementation optimized for <see cref="Rgba32"/>.
/// <see cref="PixelOperations{TPixel}"/> implementation optimized for <see cref="Rgba32"/>.
/// </summary>
internal class BulkOperations : BulkPixelOperations<Rgba32>
internal class PixelOperations : PixelOperations<Rgba32>
{
/// <summary>
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>

2
src/ImageSharp/PixelFormats/Rgba32.cs

@ -202,7 +202,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Rgba32> CreateBulkOperations() => new BulkOperations();
public PixelOperations<Rgba32> CreateBulkOperations() => new PixelOperations();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Rgba64.cs

@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Rgba64> CreateBulkOperations() => new BulkPixelOperations<Rgba64>();
public PixelOperations<Rgba64> CreateBulkOperations() => new PixelOperations<Rgba64>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

4
src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs → src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs

@ -13,9 +13,9 @@ namespace ImageSharp.PixelFormats
public partial struct RgbaVector
{
/// <summary>
/// <see cref="BulkPixelOperations{TPixel}"/> implementation optimized for <see cref="RgbaVector"/>.
/// <see cref="PixelOperations{TPixel}"/> implementation optimized for <see cref="RgbaVector"/>.
/// </summary>
internal class BulkOperations : BulkPixelOperations<RgbaVector>
internal class PixelOperations : PixelOperations<RgbaVector>
{
/// <inheritdoc />
internal override unsafe void ToVector4(BufferSpan<RgbaVector> sourceColors, BufferSpan<Vector4> destVectors, int count)

2
src/ImageSharp/PixelFormats/RgbaVector.cs

@ -208,7 +208,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<RgbaVector> CreateBulkOperations() => new RgbaVector.BulkOperations();
public PixelOperations<RgbaVector> CreateBulkOperations() => new RgbaVector.PixelOperations();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Short2.cs

@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Short2> CreateBulkOperations() => new BulkPixelOperations<Short2>();
public PixelOperations<Short2> CreateBulkOperations() => new PixelOperations<Short2>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Short4.cs

@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats
}
/// <inheritdoc />
public BulkPixelOperations<Short4> CreateBulkOperations() => new BulkPixelOperations<Short4>();
public PixelOperations<Short4> CreateBulkOperations() => new PixelOperations<Short4>();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs

@ -11,7 +11,7 @@ namespace ImageSharp.PixelFormats
/// Transform algorithms that match the equations defined in the W3C Compositing and Blending Level 1 specification.
/// <see href="https://www.w3.org/TR/compositing-1/"/>
/// </summary>
public class Vector4BlendTransforms
internal class Vector4BlendTransforms
{
/// <summary>
/// The blending formula simply selects the source vector.

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)
{
new VignetteProcessor<TPixel>(veryDarkOrange).Apply(source, sourceRectangle);
new GlowProcessor<TPixel>(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle);
new GlowProcessorParallel<TPixel>(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle);
}
}
}

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

@ -65,25 +65,31 @@ namespace ImageSharp.Processing.Processors
startY = 0;
}
int width = maxX - minX;
using (Buffer<TPixel> rowColors = new Buffer<TPixel>(width))
using (Buffer<float> amounts = new Buffer<float>(width))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
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;
}
});
for (int i = 0; i < width; i++)
{
rowColors[i] = glowColor;
}
// TODO move GraphicOptions into core so all processes can use it.
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Default);
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));
}
BufferSpan<TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width);
blender.Compose(destination, destination, rowColors, amounts);
}
}
}
}

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

@ -0,0 +1,100 @@
// <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;
}
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(PixelBlenderMode.Default);
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;
}
});
}
}
}
}

2
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -123,7 +123,7 @@ namespace ImageSharp.Processing.Processors
{
BufferSpan<TPixel> sourceRow = sourcePixels.GetRowSpan(y);
BulkPixelOperations<TPixel>.Instance.ToVector4(
PixelOperations<TPixel>.Instance.ToVector4(
sourceRow,
tempRowBuffer,
sourceRow.Length);

4
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs

@ -47,13 +47,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new BulkPixelOperations<TPixel>().PackFromXyzwBytes(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().PackFromXyzwBytes(this.source, this.destination, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
BulkPixelOperations<TPixel>.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs

@ -47,13 +47,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new BulkPixelOperations<TPixel>().ToVector4(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().ToVector4(this.source, this.destination, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
BulkPixelOperations<TPixel>.Instance.ToVector4(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.ToVector4(this.source, this.destination, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs

@ -45,13 +45,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new BulkPixelOperations<TPixel>().ToXyzBytes(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().ToXyzBytes(this.source, this.destination, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
BulkPixelOperations<TPixel>.Instance.ToXyzBytes(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.ToXyzBytes(this.source, this.destination, this.Count);
}
}

4
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs

@ -49,13 +49,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Benchmark]
public void CommonBulk()
{
new BulkPixelOperations<TPixel>().ToXyzwBytes(this.source, this.destination, this.Count);
new PixelOperations<TPixel>().ToXyzwBytes(this.source, this.destination, this.Count);
}
[Benchmark]
public void OptimizedBulk()
{
BulkPixelOperations<TPixel>.Instance.ToXyzwBytes(this.source, this.destination, this.Count);
PixelOperations<TPixel>.Instance.ToXyzwBytes(this.source, this.destination, this.Count);
}
}

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

@ -0,0 +1,48 @@
// <copyright file="Crop.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Benchmarks
{
using BenchmarkDotNet.Attributes;
using ImageSharp.PixelFormats;
using ImageSharp.Drawing;
using ImageSharp.Processing.Processors;
using CoreImage = ImageSharp.Image;
using CoreSize = ImageSharp.Size;
public class Glow : BenchmarkBase
{
private GlowProcessor<Rgba32> bulk;
private GlowProcessorParallel<Rgba32> parallel;
[Setup]
public void Setup()
{
this.bulk = new GlowProcessor<Rgba32>(NamedColors<Rgba32>.Beige) { Radius = 800 * .5f, };
this.parallel = new GlowProcessorParallel<Rgba32>(NamedColors<Rgba32>.Beige) { Radius = 800 * .5f, };
}
[Benchmark(Description = "ImageSharp Glow - Bulk")]
public CoreSize GlowBulk()
{
using (CoreImage image = new CoreImage(800, 800))
{
image.ApplyProcessor(bulk, image.Bounds);
return new CoreSize(image.Width, image.Height);
}
}
[Benchmark(Description = "ImageSharp Glow - Parallel")]
public CoreSize GLowSimple()
{
using (CoreImage image = new CoreImage(800, 800))
{
image.ApplyProcessor(parallel, image.Bounds);
return new CoreSize(image.Width, image.Height);
}
}
}
}

12
tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs → tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs

@ -10,7 +10,7 @@ namespace ImageSharp.Tests.Colors
using Xunit;
using Xunit.Abstractions;
public class BulkPixelOperationsTests
public class PixelOperationsTests
{
public class Color32 : BulkPixelOperationsTests<Rgba32>
{
@ -25,7 +25,7 @@ namespace ImageSharp.Tests.Colors
[Fact]
public void IsSpecialImplementation()
{
Assert.IsType<Rgba32.BulkOperations>(BulkPixelOperations<Rgba32>.Instance);
Assert.IsType<Rgba32.PixelOperations>(PixelOperations<Rgba32>.Instance);
}
[Fact]
@ -37,7 +37,7 @@ namespace ImageSharp.Tests.Colors
TestOperation(
source,
expected,
(s, d) => Rgba32.BulkOperations.ToVector4SimdAligned(s, d, 64)
(s, d) => Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64)
);
}
@ -54,7 +54,7 @@ namespace ImageSharp.Tests.Colors
times,
() =>
{
BulkPixelOperations<Rgba32>.Instance.ToVector4(source, dest, count);
PixelOperations<Rgba32>.Instance.ToVector4(source, dest, count);
});
}
}
@ -76,7 +76,7 @@ namespace ImageSharp.Tests.Colors
public void GetGlobalInstance<TPixel>(TestImageProvider<TPixel> dummy)
where TPixel : struct, IPixel<TPixel>
{
Assert.NotNull(BulkPixelOperations<TPixel>.Instance);
Assert.NotNull(PixelOperations<TPixel>.Instance);
}
}
@ -90,7 +90,7 @@ namespace ImageSharp.Tests.Colors
public static TheoryData<int> ArraySizesData => new TheoryData<int> { 7, 16, 1111 };
private static BulkPixelOperations<TPixel> Operations => BulkPixelOperations<TPixel>.Instance;
private static PixelOperations<TPixel> Operations => PixelOperations<TPixel>.Instance;
internal static TPixel[] CreateExpectedPixelData(Vector4[] source)
{

6
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -6,6 +6,9 @@
<DebugType Condition="$(codecov) == ''">portable</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<None Include="PixelFormats\PixelOperations.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="xunit" Version="2.2.0" />
@ -24,4 +27,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="PixelFormats\PixelBlenders\" />
</ItemGroup>
</Project>

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

@ -0,0 +1,53 @@

namespace ImageSharp.Tests.PixelFormats
{
using System;
using System.Collections.Generic;
using System.Text;
using ImageSharp.PixelFormats;
using ImageSharp.PixelFormats.PixelBlenders;
using ImageSharp.Tests.TestUtilities;
using Xunit;
public class PixelOperations
{
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<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 }
};
[Theory]
[MemberData(nameof(blenderMappings))]
public void ReturnsCorrectBlender<TPixel>(TestPixel<TPixel> pixel, Type type, PixelBlenderMode mode)
where TPixel : struct, IPixel<TPixel>
{
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode);
Assert.IsType(type, blender);
}
}
}

2
tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs

@ -71,7 +71,7 @@ namespace ImageSharp.Tests
using (Image image = file.CreateImage())
using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{
image.Glow(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2))
image.Glow(new Rectangle(image.Width / 8, image.Height / 8, image.Width / 2, image.Height / 2))
.Save(output);
}
}

62
tests/ImageSharp.Tests/TestUtilities/TestPixel.cs

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Text;
using ImageSharp.PixelFormats;
using Xunit.Abstractions;
namespace ImageSharp.Tests.TestUtilities
{
public class TestPixel<TPixel> : IXunitSerializable
where TPixel : struct, IPixel<TPixel>
{
public TestPixel()
{
}
public TestPixel(float red, float green, float blue, float alpha)
{
this.Red = red;
this.Green = green;
this.Blue = blue;
this.Alpha = alpha;
}
public float Red { get; set; }
public float Green { get; set; }
public float Blue { get; set; }
public float Alpha { get; set; }
public static implicit operator TPixel(TestPixel<TPixel> d)
{
return d?.AsPixel() ?? default(TPixel);
}
public TPixel AsPixel()
{
TPixel pix = default(TPixel);
pix.PackFromVector4(new System.Numerics.Vector4( this.Red, this.Green, this.Blue, this.Alpha));
return pix;
}
public void Deserialize(IXunitSerializationInfo info)
{
this.Red = info.GetValue<float>("red");
this.Green = info.GetValue<float>("green");
this.Blue = info.GetValue<float>("blue");
this.Alpha = info.GetValue<float>("alpha");
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue("red", this.Red);
info.AddValue("green", this.Green);
info.AddValue("blue", this.Blue);
info.AddValue("alpha", this.Alpha);
}
public override string ToString()
{
return $"{typeof(TPixel).Name}{this.AsPixel().ToString()}";
}
}
}
Loading…
Cancel
Save