Browse Source

DrawImageProcessor + formatting

af/merge-core
Anton Firszov 8 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers; using System.Buffers;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary> {
/// Combines two images together by blending the pixels. /// <summary>
/// </summary> /// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam> /// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam> /// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst> internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst> where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc> where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class. /// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// </summary> /// </summary>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the 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="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> /// <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) public DrawImageProcessor(Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image; this.Image = image;
this.Opacity = opacity; this.Opacity = opacity;
this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location; this.Location = location;
} }
/// <summary> /// <summary>
/// Gets the image to blend /// Gets the image to blend
/// </summary> /// </summary>
public Image<TPixelSrc> Image { get; } public Image<TPixelSrc> Image { get; }
/// <summary> /// <summary>
/// Gets the opacity of the image to blend /// Gets the opacity of the image to blend
/// </summary> /// </summary>
public float Opacity { get; } public float Opacity { get; }
/// <summary> /// <summary>
/// Gets the pixel blender /// Gets the pixel blender
/// </summary> /// </summary>
public PixelBlender<TPixelDst> Blender { get; } public PixelBlender<TPixelDst> Blender { get; }
/// <summary> /// <summary>
/// Gets the location to draw the blended image /// Gets the location to draw the blended image
/// </summary> /// </summary>
public Point Location { get; } public Point Location { get; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
{ {
Image<TPixelSrc> targetImage = this.Image; Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixelDst> blender = this.Blender; PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y; int locationY = this.Location.Y;
// Align start/end positions. // Align start/end positions.
Rectangle bounds = targetImage.Bounds(); Rectangle bounds = targetImage.Bounds();
int minX = Math.Max(this.Location.X, sourceRectangle.X); int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
int targetX = minX - this.Location.X; int targetX = minX - this.Location.X;
int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
int width = maxX - minX; int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
ParallelFor.WithConfiguration( var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
minY,
maxY, ParallelHelper.IterateRows(
configuration, workingRect,
y => configuration,
{ rows =>
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width); {
Span<TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); for (int y = rows.Min; y < rows.Max; y++)
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity); {
}); 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers; using System.Buffers;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ {
/// <summary> /// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source. /// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel> internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// The brush. /// The brush.
/// </summary> /// </summary>
private readonly IBrush<TPixel> brush; private readonly IBrush<TPixel> brush;
private readonly GraphicsOptions options; private readonly GraphicsOptions options;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FillProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="FillProcessor{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="brush">The brush to source pixel colors from.</param> /// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="options">The options</param> /// <param name="options">The options</param>
public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options) public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options)
{ {
this.brush = brush; this.brush = brush;
this.options = options; this.options = options;
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
int startX = sourceRectangle.X; int startX = sourceRectangle.X;
int endX = sourceRectangle.Right; int endX = sourceRectangle.Right;
int startY = sourceRectangle.Y; int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom; int endY = sourceRectangle.Bottom;
// Align start/end positions. // Align start/end positions.
int minX = Math.Max(0, startX); int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX); int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY); int maxY = Math.Min(source.Height, endY);
int width = maxX - minX; int width = maxX - minX;
// If there's no reason for blending, then avoid it. // If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)) if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush))
{ {
ParallelFor.WithConfiguration( ParallelFor.WithConfiguration(
minY, minY,
maxY, maxY,
configuration, configuration,
y => y =>
{ {
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
}); });
} }
else else
{ {
// Reset offset if necessary. // Reset offset if necessary.
if (minX > 0) if (minX > 0)
{ {
startX = 0; startX = 0;
} }
if (minY > 0) if (minY > 0)
{ {
startY = 0; startY = 0;
} }
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width)) using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator( using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source, source,
sourceRectangle, sourceRectangle,
this.options)) this.options))
{ {
amount.GetSpan().Fill(1f); amount.GetSpan().Fill(1f);
ParallelFor.WithConfiguration( ParallelFor.WithConfiguration(
minY, minY,
maxY, maxY,
configuration, configuration,
y => y =>
{ {
int offsetY = y - startY; int offsetY = y - startY;
int offsetX = minX - startX; int offsetX = minX - startX;
applicator.Apply(amount.GetSpan(), offsetX, offsetY); applicator.Apply(amount.GetSpan(), offsetX, offsetY);
}); });
} }
} }
} }
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush) private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
{ {
solidBrush = this.brush as SolidBrush<TPixel>; solidBrush = this.brush as SolidBrush<TPixel>;
if (solidBrush == null) if (solidBrush == null)
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
return false; 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] [Fact]
public void ImageShouldBeFloodFilledWithPercent10() 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[,] new[,]
{ {
{ Rgba32.HotPink , 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.LimeGreen, Rgba32.LimeGreen },
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , 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 }
}); });
} }
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithPercent10Transparent() public void ImageShouldBeFloodFilledWithPercent10Transparent()
{ {
Test("Percent10_Transparent", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink), this.Test(
new Rgba32[,] { "Percent10_Transparent",
{ Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Rgba32.Blue,
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Brushes.Percent10(Rgba32.HotPink),
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}, new Rgba32[,]
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithPercent20() public void ImageShouldBeFloodFilledWithPercent20()
{ {
Test("Percent20", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen), this.Test(
new Rgba32[,] { "Percent20",
{ Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, Rgba32.Blue,
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen}, Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen),
{ Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, new Rgba32[,]
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithPercent20_transparent() public void ImageShouldBeFloodFilledWithPercent20_transparent()
{ {
Test("Percent20_Transparent", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink), this.Test(
new Rgba32[,] { "Percent20_Transparent",
{ Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Rgba32.Blue,
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}, Brushes.Percent20(Rgba32.HotPink),
{ Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, new Rgba32[,]
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithHorizontal() public void ImageShouldBeFloodFilledWithHorizontal()
{ {
Test("Horizontal", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen), this.Test(
new Rgba32[,] { "Horizontal",
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, Rgba32.Blue,
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen),
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, new Rgba32[,]
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithHorizontal_transparent() public void ImageShouldBeFloodFilledWithHorizontal_transparent()
{ {
Test("Horizontal_Transparent", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink), this.Test(
new Rgba32[,] { "Horizontal_Transparent",
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Rgba32.Blue,
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, Brushes.Horizontal(Rgba32.HotPink),
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, new Rgba32[,]
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithMin() public void ImageShouldBeFloodFilledWithMin()
{ {
Test("Min", Rgba32.Blue, Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen), this.Test(
new Rgba32[,] { "Min",
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, Rgba32.Blue,
{ Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen),
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen}, new Rgba32[,]
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithMin_transparent() public void ImageShouldBeFloodFilledWithMin_transparent()
{ {
Test("Min_Transparent", Rgba32.Blue, Brushes.Min(Rgba32.HotPink), this.Test(
new Rgba32[,] { "Min_Transparent",
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Rgba32.Blue,
{ Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Brushes.Min(Rgba32.HotPink),
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue}, new Rgba32[,]
{ Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithVertical() public void ImageShouldBeFloodFilledWithVertical()
{ {
Test("Vertical", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen), this.Test(
new Rgba32[,] { "Vertical",
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, Rgba32.Blue,
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen),
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, 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 },
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }
});
} }
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithVertical_transparent() public void ImageShouldBeFloodFilledWithVertical_transparent()
{ {
Test("Vertical_Transparent", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink), this.Test(
new Rgba32[,] { "Vertical_Transparent",
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, Rgba32.Blue,
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, Brushes.Vertical(Rgba32.HotPink),
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, 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 },
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }
});
} }
[Fact] [Fact]
public void ImageShouldBeFloodFilledWithForwardDiagonal() public void ImageShouldBeFloodFilledWithForwardDiagonal()
{ {
Test("ForwardDiagonal", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), this.Test(
new Rgba32[,] { "ForwardDiagonal",
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink}, Rgba32.Blue,
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen}, Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen),
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, new Rgba32[,]
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithForwardDiagonal_transparent() public void ImageShouldBeFloodFilledWithForwardDiagonal_transparent()
{ {
Test("ForwardDiagonal_Transparent", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink), this.Test(
new Rgba32[,] { "ForwardDiagonal_Transparent",
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink}, Rgba32.Blue,
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue}, Brushes.ForwardDiagonal(Rgba32.HotPink),
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, new Rgba32[,]
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithBackwardDiagonal() public void ImageShouldBeFloodFilledWithBackwardDiagonal()
{ {
Test("BackwardDiagonal", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), this.Test(
new Rgba32[,] { "BackwardDiagonal",
{ Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, Rgba32.Blue,
{ Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen),
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen}, new Rgba32[,]
{ Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink} {
}); { 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] [Fact]
public void ImageShouldBeFloodFilledWithBackwardDiagonal_transparent() public void ImageShouldBeFloodFilledWithBackwardDiagonal_transparent()
{ {
Test("BackwardDiagonal_Transparent", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink), this.Test(
new Rgba32[,] { "BackwardDiagonal_Transparent",
{ Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, Rgba32.Blue,
{ Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, Brushes.BackwardDiagonal(Rgba32.HotPink),
{ Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue}, new Rgba32[,]
{ Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink} {
}); { 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. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Drawing namespace SixLabors.ImageSharp.Tests.Drawing
{ {
using System;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
[GroupOutput("Drawing")] [GroupOutput("Drawing")]
public class FillSolidBrushTests public class FillSolidBrushTests
{ {
@ -55,7 +56,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Theory] [Theory]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")]
[WithSolidFilledImages(16, 16, "Yellow", PixelTypes.Rgba32, "Khaki")] [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> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
@ -63,7 +66,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>(newColorName); TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>(newColorName);
image.Mutate(c => c.Fill(color)); 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); image.ComparePixelBufferTo(color);
} }
} }
@ -84,7 +91,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Theory] [Theory]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)]
[WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] [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> where TPixel : struct, IPixel<TPixel>
{ {
FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; 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, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, { false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, { false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, { false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f },
{ false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, { false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f },
{ false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f },
{ false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, { false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f },
{ false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, { true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, { true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, { true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, { true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f },
{ true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f },
{ true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, { true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f },
{ true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f },
{ true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, { true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f },
@ -155,8 +162,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var options = new GraphicsOptions(false) var options = new GraphicsOptions(false)
{ {
ColorBlendingMode = blenderMode, ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage
BlendPercentage = blendPercentage
}; };
if (triggerFillRegion) if (triggerFillRegion)
@ -185,11 +191,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
appendPixelTypeToFileName: false, appendPixelTypeToFileName: false,
appendSourceFileOrDescription: 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); TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage);
image.ComparePixelBufferTo(expectedPixel); image.ComparePixelBufferTo(expectedPixel);
} }
} }
} }
} }
Loading…
Cancel
Save