Browse Source

DrawImageProcessor + formatting

af/merge-core
Anton Firszov 7 years ago
parent
commit
7277ca2a29
  1. 178
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  2. 214
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  3. 255
      tests/ImageSharp.Tests/Drawing/FillPatternTests.cs
  4. 44
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs

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

@ -1,95 +1,101 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
/// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
where TPixelSrc : struct, IPixel<TPixelSrc>
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image;
this.Opacity = opacity;
this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location;
}
/// <summary>
/// Gets the image to blend
/// </summary>
public Image<TPixelSrc> Image { get; }
/// <summary>
/// Gets the opacity of the image to blend
/// </summary>
public float Opacity { get; }
/// <summary>
/// Gets the pixel blender
/// </summary>
public PixelBlender<TPixelDst> Blender { get; }
/// <summary>
/// Gets the location to draw the blended image
/// </summary>
public Point Location { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
{
Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y;
// Align start/end positions.
Rectangle bounds = targetImage.Bounds();
int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
int targetX = minX - this.Location.X;
int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
int width = maxX - minX;
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image;
this.Opacity = opacity;
this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location;
}
/// <summary>
/// Gets the image to blend
/// </summary>
public Image<TPixelSrc> Image { get; }
/// <summary>
/// Gets the opacity of the image to blend
/// </summary>
public float Opacity { get; }
/// <summary>
/// Gets the pixel blender
/// </summary>
public PixelBlender<TPixelDst> Blender { get; }
/// <summary>
/// Gets the location to draw the blended image
/// </summary>
public Point Location { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
{
Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y;
// Align start/end positions.
Rectangle bounds = targetImage.Bounds();
int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
int targetX = minX - this.Location.X;
int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity);
});
}
}
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
ParallelHelper.IterateRows(
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity);
}
});
}
}
}

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

@ -1,107 +1,107 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// 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>
/// <param name="options">The options</param>
public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options)
{
this.brush = brush;
this.options = options;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
// 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);
int width = maxX - minX;
// If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush))
{
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
});
}
else
{
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
amount.GetSpan().Fill(1f);
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount.GetSpan(), offsetX, offsetY);
});
}
}
}
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
{
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// 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>
/// <param name="options">The options</param>
public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options)
{
this.brush = brush;
this.options = options;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
// 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);
int width = maxX - minX;
// If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush))
{
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
});
}
else
{
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
amount.GetSpan().Fill(1f);
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount.GetSpan(), offsetX, offsetY);
});
}
}
}
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
{
solidBrush = this.brush as SolidBrush<TPixel>;
if (solidBrush == null)
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
return false;
}
return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
}

255
tests/ImageSharp.Tests/Drawing/FillPatternTests.cs

@ -54,172 +54,225 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Fact]
public void ImageShouldBeFloodFilledWithPercent10()
{
this.Test("Percent10", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink, Rgba32.LimeGreen),
this.Test(
"Percent10",
Rgba32.Blue,
Brushes.Percent10(Rgba32.HotPink, Rgba32.LimeGreen),
new[,]
{
{ Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}
});
{
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithPercent10Transparent()
{
Test("Percent10_Transparent", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}
});
this.Test(
"Percent10_Transparent",
Rgba32.Blue,
Brushes.Percent10(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithPercent20()
{
Test("Percent20", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,] {
{ Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen},
{ Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen}
});
this.Test(
"Percent20",
Rgba32.Blue,
Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,]
{
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen },
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithPercent20_transparent()
{
Test("Percent20_Transparent", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue},
{ Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}
});
this.Test(
"Percent20_Transparent",
Rgba32.Blue,
Brushes.Percent20(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue },
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithHorizontal()
{
Test("Horizontal", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,] {
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink},
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen}
});
this.Test(
"Horizontal",
Rgba32.Blue,
Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,]
{
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithHorizontal_transparent()
{
Test("Horizontal_Transparent", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink},
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue}
});
this.Test(
"Horizontal_Transparent",
Rgba32.Blue,
Brushes.Horizontal(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithMin()
{
Test("Min", Rgba32.Blue, Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,] {
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen},
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}
});
this.Test(
"Min",
Rgba32.Blue,
Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,]
{
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithMin_transparent()
{
Test("Min_Transparent", Rgba32.Blue, Brushes.Min(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue},
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink},
});
this.Test(
"Min_Transparent",
Rgba32.Blue,
Brushes.Min(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink },
});
}
[Fact]
public void ImageShouldBeFloodFilledWithVertical()
{
Test("Vertical", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,] {
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}
});
this.Test(
"Vertical",
Rgba32.Blue,
Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,]
{
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithVertical_transparent()
{
Test("Vertical_Transparent", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}
});
this.Test(
"Vertical_Transparent",
Rgba32.Blue,
Brushes.Vertical(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithForwardDiagonal()
{
Test("ForwardDiagonal", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,] {
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}
});
this.Test(
"ForwardDiagonal",
Rgba32.Blue,
Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,]
{
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithForwardDiagonal_transparent()
{
Test("ForwardDiagonal_Transparent", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}
});
this.Test(
"ForwardDiagonal_Transparent",
Rgba32.Blue,
Brushes.ForwardDiagonal(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithBackwardDiagonal()
{
Test("BackwardDiagonal", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,] {
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen},
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink}
});
this.Test(
"BackwardDiagonal",
Rgba32.Blue,
Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen),
new Rgba32[,]
{
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink }
});
}
[Fact]
public void ImageShouldBeFloodFilledWithBackwardDiagonal_transparent()
{
Test("BackwardDiagonal_Transparent", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink),
new Rgba32[,] {
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue},
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink}
});
this.Test(
"BackwardDiagonal_Transparent",
Rgba32.Blue,
Brushes.BackwardDiagonal(Rgba32.HotPink),
new Rgba32[,]
{
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue },
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink }
});
}
}
}
}

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

@ -1,20 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using SixLabors.Shapes;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Drawing
{
using System;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
[GroupOutput("Drawing")]
public class FillSolidBrushTests
{
@ -55,7 +56,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Theory]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")]
[WithSolidFilledImages(16, 16, "Yellow", PixelTypes.Rgba32, "Khaki")]
public void WhenColorIsOpaque_OverridePreviousColor<TPixel>(TestImageProvider<TPixel> provider, string newColorName)
public void WhenColorIsOpaque_OverridePreviousColor<TPixel>(
TestImageProvider<TPixel> provider,
string newColorName)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
@ -63,7 +66,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>(newColorName);
image.Mutate(c => c.Fill(color));
image.DebugSave(provider, newColorName, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false);
image.DebugSave(
provider,
newColorName,
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
image.ComparePixelBufferTo(color);
}
}
@ -84,7 +91,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Theory]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)]
public void FillRegion_WorksOnWrappedMemoryImage<TPixel>(TestImageProvider<TPixel> provider, int x0, int y0, int w, int h)
public void FillRegion_WorksOnWrappedMemoryImage<TPixel>(
TestImageProvider<TPixel> provider,
int x0,
int y0,
int w,
int h)
where TPixel : struct, IPixel<TPixel>
{
FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})";
@ -105,27 +117,22 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ false, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f },
@ -155,8 +162,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var options = new GraphicsOptions(false)
{
ColorBlendingMode = blenderMode,
BlendPercentage = blendPercentage
ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage
};
if (triggerFillRegion)
@ -185,11 +191,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(blenderMode, PixelAlphaCompositionMode.SrcOver);
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(
blenderMode,
PixelAlphaCompositionMode.SrcOver);
TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage);
image.ComparePixelBufferTo(expectedPixel);
}
}
}
}
}
Loading…
Cancel
Save