Browse Source

Merge branch 'master' into js/managed-zlib

af/merge-core
James Jackson-South 6 years ago
parent
commit
b2bff7fd4c
  1. 53
      src/ImageSharp.Drawing/Common/Extensions/GraphicsOptionsExtensions.cs
  2. 6
      src/ImageSharp.Drawing/Primitives/ShapeRegion.cs
  3. 61
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  4. 41
      src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs
  5. 40
      src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs
  6. 2
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs
  7. 2
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs
  8. 2
      src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs
  9. 2
      src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs
  10. 8
      src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs
  11. 2
      src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs
  12. 2
      src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs
  13. 2
      src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs
  14. 12
      src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs
  15. 49
      src/ImageSharp.Drawing/Processing/GradientBrush.cs
  16. 16
      src/ImageSharp.Drawing/Processing/IBrush.cs
  17. 57
      src/ImageSharp.Drawing/Processing/ImageBrush.cs
  18. 41
      src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs
  19. 34
      src/ImageSharp.Drawing/Processing/PathGradientBrush.cs
  20. 56
      src/ImageSharp.Drawing/Processing/PatternBrush.cs
  21. 4
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  22. 13
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
  23. 4
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  24. 6
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
  25. 8
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
  26. 30
      src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs
  27. 57
      src/ImageSharp.Drawing/Processing/RecolorBrush.cs
  28. 76
      src/ImageSharp.Drawing/Processing/SolidBrush.cs
  29. 180
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  30. 29
      src/ImageSharp.Drawing/Utils/NumberUtils.cs
  31. 2
      src/ImageSharp.Drawing/Utils/QuickSort.cs
  32. 35
      src/ImageSharp/Color/Color.cs
  33. 178
      src/ImageSharp/GraphicsOptions.cs
  34. 27
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs
  35. 8
      src/ImageSharp/Primitives/DenseMatrix{T}.cs
  36. 12
      src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs
  37. 18
      src/ImageSharp/Processing/Extensions/GlowExtensions.cs
  38. 18
      src/ImageSharp/Processing/Extensions/VignetteExtensions.cs
  39. 4
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs
  40. 14
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  41. 10
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  42. 10
      tests/ImageSharp.Benchmarks/Drawing/DrawText.cs
  43. 14
      tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs
  44. 2
      tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
  45. 22
      tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs
  46. 2
      tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs
  47. 45
      tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs
  48. 2
      tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs
  49. 22
      tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs
  50. 24
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs
  51. 17
      tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs
  52. 17
      tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs
  53. 17
      tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs
  54. 17
      tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs
  55. 26
      tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs
  56. 29
      tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs
  57. 12
      tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
  58. 9
      tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
  59. 183
      tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs
  60. 93
      tests/ImageSharp.Tests/GraphicsOptionsTests.cs
  61. 4
      tests/ImageSharp.Tests/Issues/Issue412.cs
  62. 11
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs
  63. 2
      tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs
  64. 24
      tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs
  65. 23
      tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs
  66. 23
      tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs
  67. 21
      tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs
  68. 2
      tests/Images/External

53
src/ImageSharp.Drawing/Common/Extensions/GraphicsOptionsExtensions.cs

@ -0,0 +1,53 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extensions methods fpor the <see cref="GraphicsOptions"/> class.
/// </summary>
internal static class GraphicsOptionsExtensions
{
/// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </summary>
/// <param name="options">The graphics options.</param>
/// <param name="color">The source color.</param>
/// <returns>true if the color can be considered opaque</returns>
/// <remarks>
/// Blending and composition is an expensive operation, in some cases, like
/// filling with a solid color, the blending can be avoided by a plain color replacement.
/// This method can be useful for such processors to select the fast path.
/// </remarks>
public static bool IsOpaqueColorWithoutBlending(this GraphicsOptions options, Color color)
{
if (options.ColorBlendingMode != PixelColorBlendingMode.Normal)
{
return false;
}
if (options.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver
&& options.AlphaCompositionMode != PixelAlphaCompositionMode.Src)
{
return false;
}
const float Opaque = 1F;
if (options.BlendPercentage != Opaque)
{
return false;
}
if (((Vector4)color).W != Opaque)
{
return false;
}
return true;
}
}
}

6
src/ImageSharp.Drawing/Primitives/ShapeRegion.cs

@ -1,4 +1,4 @@
// 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;
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Primitives
using (IMemoryOwner<PointF> tempBuffer = configuration.MemoryAllocator.Allocate<PointF>(buffer.Length)) using (IMemoryOwner<PointF> tempBuffer = configuration.MemoryAllocator.Allocate<PointF>(buffer.Length))
{ {
Span<PointF> innerBuffer = tempBuffer.GetSpan(); Span<PointF> innerBuffer = tempBuffer.Memory.Span;
int count = this.Shape.FindIntersections(start, end, innerBuffer); int count = this.Shape.FindIntersections(start, end, innerBuffer);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -61,4 +61,4 @@ namespace SixLabors.ImageSharp.Primitives
} }
} }
} }
} }

61
src/ImageSharp.Drawing/Processing/BrushApplicator.cs

@ -1,68 +1,85 @@
// 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 SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
{ {
/// <summary> /// <summary>
/// primitive that converts a point in to a color for discovering the fill color based on an implementation /// A primitive that converts a point into a color for discovering the fill color based on an implementation.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <seealso cref="System.IDisposable" /> /// <seealso cref="IDisposable" />
public abstract class BrushApplicator<TPixel> : IDisposable // disposable will be required if/when there is an ImageBrush public abstract class BrushApplicator<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BrushApplicator{TPixel}"/> class. /// Initializes a new instance of the <see cref="BrushApplicator{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The graphics options.</param>
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
/// <param name="options">The options.</param> internal BrushApplicator(Configuration configuration, GraphicsOptions options, ImageFrame<TPixel> target)
internal BrushApplicator(ImageFrame<TPixel> target, GraphicsOptions options)
{ {
this.Configuration = configuration;
this.Target = target; this.Target = target;
this.Options = options; this.Options = options;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options); this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
} }
/// <summary> /// <summary>
/// Gets the blender /// Gets the configuration instance to use when performing operations.
/// </summary>
protected Configuration Configuration { get; }
/// <summary>
/// Gets the pixel blender.
/// </summary> /// </summary>
internal PixelBlender<TPixel> Blender { get; } internal PixelBlender<TPixel> Blender { get; }
/// <summary> /// <summary>
/// Gets the destination /// Gets the target image.
/// </summary> /// </summary>
protected ImageFrame<TPixel> Target { get; } protected ImageFrame<TPixel> Target { get; }
/// <summary> /// <summary>
/// Gets the blend percentage /// Gets thegraphics options
/// </summary> /// </summary>
protected GraphicsOptions Options { get; } protected GraphicsOptions Options { get; }
/// <summary> /// <summary>
/// Gets the color for a single pixel. /// Gets the overlay pixel at the specified position.
/// </summary> /// </summary>
/// <param name="x">The x coordinate.</param> /// <param name="x">The x-coordinate.</param>
/// <param name="y">The y coordinate.</param> /// <param name="y">The y-coordinate.</param>
/// <returns>The a <typeparamref name="TPixel"/> that should be applied to the pixel.</returns> /// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
internal abstract TPixel this[int x, int y] { get; } internal abstract TPixel this[int x, int y] { get; }
/// <inheritdoc/> /// <inheritdoc/>
public abstract void Dispose(); public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">Whether to dispose managed and unmanaged objects.</param>
protected virtual void Dispose(bool disposing)
{
}
/// <summary> /// <summary>
/// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush. /// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
/// </summary> /// </summary>
/// <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="scanline">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="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> /// <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> /// <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(Span<float> scanline, int x, int y) internal virtual void Apply(Span<float> scanline, int x, int y)
{ {
@ -71,8 +88,8 @@ namespace SixLabors.ImageSharp.Processing
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length)) using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length)) using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{ {
Span<float> amountSpan = amountBuffer.GetSpan(); Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.GetSpan(); Span<TPixel> overlaySpan = overlay.Memory.Span;
for (int i = 0; i < scanline.Length; i++) for (int i = 0; i < scanline.Length; i++)
{ {
@ -89,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); this.Blender.Blend(this.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan);
} }
} }
} }

41
src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs

@ -1,4 +1,4 @@
// 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;
@ -47,12 +47,14 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>( public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region) =>
GraphicsOptions options) =>
new RadialGradientBrushApplicator<TPixel>( new RadialGradientBrushApplicator<TPixel>(
source, configuration,
options, options,
source,
this.center, this.center,
this.referenceAxisEnd, this.referenceAxisEnd,
this.axisRatio, this.axisRatio,
@ -86,24 +88,26 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class. /// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="target">The target image</param> /// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The options</param> /// <param name="options">The graphics options.</param>
/// <param name="center">Center of the ellipse</param> /// <param name="target">The target image.</param>
/// <param name="center">Center of the ellipse.</param>
/// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param> /// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param>
/// <param name="axisRatio"> /// <param name="axisRatio">
/// Ratio of the axis length's. Used to determine the length of the second axis, /// Ratio of the axis length's. Used to determine the length of the second axis,
/// the first is defined by <see cref="center"/> and <see cref="referenceAxisEnd"/>.</param> /// the first is defined by <see cref="center"/> and <see cref="referenceAxisEnd"/>.</param>
/// <param name="colorStops">Definition of colors</param> /// <param name="colorStops">Definition of colors.</param>
/// <param name="repetitionMode">Defines how the gradient colors are repeated.</param> /// <param name="repetitionMode">Defines how the gradient colors are repeated.</param>
public RadialGradientBrushApplicator( public RadialGradientBrushApplicator(
ImageFrame<TPixel> target, Configuration configuration,
GraphicsOptions options, GraphicsOptions options,
ImageFrame<TPixel> target,
PointF center, PointF center,
PointF referenceAxisEnd, PointF referenceAxisEnd,
float axisRatio, float axisRatio,
ColorStop[] colorStops, ColorStop[] colorStops,
GradientRepetitionMode repetitionMode) GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode) : base(configuration, options, target, colorStops, repetitionMode)
{ {
this.center = center; this.center = center;
this.referenceAxisEnd = referenceAxisEnd; this.referenceAxisEnd = referenceAxisEnd;
@ -122,11 +126,6 @@ namespace SixLabors.ImageSharp.Processing
this.cosRotation = (float)Math.Cos(this.rotation); this.cosRotation = (float)Math.Cos(this.rotation);
} }
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc /> /// <inheritdoc />
protected override float PositionOnGradient(float xt, float yt) protected override float PositionOnGradient(float xt, float yt)
{ {
@ -139,16 +138,13 @@ namespace SixLabors.ImageSharp.Processing
float xSquared = x * x; float xSquared = x * x;
float ySquared = y * y; float ySquared = y * y;
var inBoundaryChecker = (xSquared / this.referenceRadiusSquared) return (xSquared / this.referenceRadiusSquared) + (ySquared / this.secondRadiusSquared);
+ (ySquared / this.secondRadiusSquared);
return inBoundaryChecker;
} }
private float AngleBetween(PointF junction, PointF a, PointF b) private float AngleBetween(PointF junction, PointF a, PointF b)
{ {
var vA = a - junction; PointF vA = a - junction;
var vB = b - junction; PointF vB = b - junction;
return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X); return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X);
} }
@ -156,6 +152,7 @@ namespace SixLabors.ImageSharp.Processing
PointF p1, PointF p1,
PointF p2) PointF p2)
{ {
// TODO: Can we not just use Vector2 distance here?
float dX = p1.X - p2.X; float dX = p1.X - p2.X;
float dXsquared = dX * dX; float dXsquared = dX * dX;
@ -165,4 +162,4 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
} }
} }

40
src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs

@ -22,14 +22,17 @@ namespace SixLabors.ImageSharp.Processing
public static IImageProcessingContext DrawImage( public static IImageProcessingContext DrawImage(
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image image,
float opacity) => float opacity)
source.ApplyProcessor( {
var options = new GraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor( new DrawImageProcessor(
image, image,
Point.Empty, Point.Empty,
GraphicsOptions.Default.ColorBlendingMode, options.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode, options.AlphaCompositionMode,
opacity)); opacity));
}
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
@ -49,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
image, image,
Point.Empty, Point.Empty,
colorBlending, colorBlending,
GraphicsOptions.Default.AlphaCompositionMode, new GraphicsOptions().AlphaCompositionMode,
opacity)); opacity));
/// <summary> /// <summary>
@ -100,14 +103,17 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
Image image, Image image,
Point location, Point location,
float opacity) => float opacity)
source.ApplyProcessor( {
var options = new GraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor( new DrawImageProcessor(
image, image,
location, location,
GraphicsOptions.Default.ColorBlendingMode, options.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode, options.AlphaCompositionMode,
opacity)); opacity));
}
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
@ -129,7 +135,7 @@ namespace SixLabors.ImageSharp.Processing
image, image,
location, location,
colorBlending, colorBlending,
GraphicsOptions.Default.AlphaCompositionMode, new GraphicsOptions().AlphaCompositionMode,
opacity)); opacity));
/// <summary> /// <summary>
@ -172,4 +178,4 @@ namespace SixLabors.ImageSharp.Processing
options.AlphaCompositionMode, options.AlphaCompositionMode,
options.BlendPercentage)); options.BlendPercentage));
} }
} }

2
src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext public static IImageProcessingContext
Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) => Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) =>
source.Draw(GraphicsOptions.Default, pen, paths); source.Draw(new GraphicsOptions(), pen, paths);
/// <summary> /// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness. /// Draws the outline of the polygon with the provided brush at the provided thickness.

2
src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) => public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) =>
source.Draw(GraphicsOptions.Default, pen, path); source.Draw(new GraphicsOptions(), pen, path);
/// <summary> /// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness. /// Draws the outline of the polygon with the provided brush at the provided thickness.

2
src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs

@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
IPen pen, IPen pen,
params PointF[] points) => params PointF[] points) =>
source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points))); source.Draw(new GraphicsOptions(), pen, new Polygon(new LinearLineSegment(points)));
/// <summary> /// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// Draws the provided Points as a closed Linear Polygon with the provided Pen.

2
src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) => public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) =>
source.Draw(GraphicsOptions.Default, pen, shape); source.Draw(new GraphicsOptions(), pen, shape);
/// <summary> /// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness. /// Draws the outline of the rectangle with the provided brush at the provided thickness.

8
src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing
Font font, Font font,
Color color, Color color,
PointF location) => PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, color, location); source.DrawText(new TextGraphicsOptions(), text, font, color, location);
/// <summary> /// <summary>
/// Draws the text onto the the image filled via the brush. /// Draws the text onto the the image filled via the brush.
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing
Font font, Font font,
IBrush brush, IBrush brush,
PointF location) => PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, brush, location); source.DrawText(new TextGraphicsOptions(), text, font, brush, location);
/// <summary> /// <summary>
/// Draws the text onto the the image filled via the brush. /// Draws the text onto the the image filled via the brush.
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing
Font font, Font font,
IPen pen, IPen pen,
PointF location) => PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, pen, location); source.DrawText(new TextGraphicsOptions(), text, font, pen, location);
/// <summary> /// <summary>
/// Draws the text onto the the image outlined via the pen. /// Draws the text onto the the image outlined via the pen.
@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Processing
IBrush brush, IBrush brush,
IPen pen, IPen pen,
PointF location) => PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location); source.DrawText(new TextGraphicsOptions(), text, font, brush, pen, location);
/// <summary> /// <summary>
/// Draws the text using the default resolution of <value>72dpi</value> onto the the image filled via the brush then outlined via the pen. /// Draws the text using the default resolution of <value>72dpi</value> onto the the image filled via the brush then outlined via the pen.

2
src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
IBrush brush, IBrush brush,
Action<PathBuilder> path) => Action<PathBuilder> path) =>
source.Fill(GraphicsOptions.Default, brush, path); source.Fill(new GraphicsOptions(), brush, path);
/// <summary> /// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush. /// Flood fills the image in the shape of the provided polygon with the specified brush.

2
src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
IBrush brush, IBrush brush,
IPathCollection paths) => IPathCollection paths) =>
source.Fill(GraphicsOptions.Default, brush, paths); source.Fill(new GraphicsOptions(), brush, paths);
/// <summary> /// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush. /// Flood fills the image in the shape of the provided polygon with the specified brush.

2
src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) => public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) =>
source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path)); source.Fill(new GraphicsOptions(), brush, new ShapeRegion(path));
/// <summary> /// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.. /// Flood fills the image in the shape of the provided polygon with the specified brush..

12
src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="brush">The details how to fill the region of interest.</param> /// <param name="brush">The details how to fill the region of interest.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) => public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) =>
source.Fill(GraphicsOptions.Default, brush); source.Fill(new GraphicsOptions(), brush);
/// <summary> /// <summary>
/// Flood fills the image with the specified color. /// Flood fills the image with the specified color.
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="region">The region.</param> /// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) => public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) =>
source.Fill(GraphicsOptions.Default, brush, region); source.Fill(new GraphicsOptions(), brush, region);
/// <summary> /// <summary>
/// Flood fills the image with in the region with the specified color. /// Flood fills the image with in the region with the specified color.
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options, GraphicsOptions options,
IBrush brush, IBrush brush,
Region region) => Region region) =>
source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); source.ApplyProcessor(new FillRegionProcessor(options, brush, region));
/// <summary> /// <summary>
/// Flood fills the image with the specified brush. /// Flood fills the image with the specified brush.
@ -90,6 +90,6 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
GraphicsOptions options, GraphicsOptions options,
IBrush brush) => IBrush brush) =>
source.ApplyProcessor(new FillProcessor(brush, options)); source.ApplyProcessor(new FillProcessor(options, brush));
} }
} }

49
src/ImageSharp.Drawing/Processing/GradientBrush.cs

@ -1,11 +1,9 @@
// 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.Numerics; using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
@ -38,9 +36,10 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public abstract BrushApplicator<TPixel> CreateApplicator<TPixel>( public abstract BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region)
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>; where TPixel : struct, IPixel<TPixel>;
/// <summary> /// <summary>
@ -58,27 +57,24 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GradientBrushApplicator{TPixel}"/> class. /// Initializes a new instance of the <see cref="GradientBrushApplicator{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="target">The target.</param> /// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The options.</param> /// <param name="options">The graphics options.</param>
/// <param name="target">The target image.</param>
/// <param name="colorStops">An array of color stops sorted by their position.</param> /// <param name="colorStops">An array of color stops sorted by their position.</param>
/// <param name="repetitionMode">Defines if and how the gradient should be repeated.</param> /// <param name="repetitionMode">Defines if and how the gradient should be repeated.</param>
protected GradientBrushApplicator( protected GradientBrushApplicator(
ImageFrame<TPixel> target, Configuration configuration,
GraphicsOptions options, GraphicsOptions options,
ImageFrame<TPixel> target,
ColorStop[] colorStops, ColorStop[] colorStops,
GradientRepetitionMode repetitionMode) GradientRepetitionMode repetitionMode)
: base(target, options) : base(configuration, options, target)
{ {
this.colorStops = colorStops; // TODO: requires colorStops to be sorted by position - should that be checked? this.colorStops = colorStops; // TODO: requires colorStops to be sorted by position - should that be checked?
this.repetitionMode = repetitionMode; this.repetitionMode = repetitionMode;
} }
/// <summary> /// <inheritdoc/>
/// Base implementation of the indexer for gradients
/// (follows the facade pattern, using abstract methods)
/// </summary>
/// <param name="x">X coordinate of the Pixel.</param>
/// <param name="y">Y coordinate of the Pixel.</param>
internal override TPixel this[int x, int y] internal override TPixel this[int x, int y]
{ {
get get
@ -92,10 +88,10 @@ namespace SixLabors.ImageSharp.Processing
// onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient)); // onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient));
break; break;
case GradientRepetitionMode.Repeat: case GradientRepetitionMode.Repeat:
positionOnCompleteGradient = positionOnCompleteGradient % 1; positionOnCompleteGradient %= 1;
break; break;
case GradientRepetitionMode.Reflect: case GradientRepetitionMode.Reflect:
positionOnCompleteGradient = positionOnCompleteGradient % 2; positionOnCompleteGradient %= 2;
if (positionOnCompleteGradient > 1) if (positionOnCompleteGradient > 1)
{ {
positionOnCompleteGradient = 2 - positionOnCompleteGradient; positionOnCompleteGradient = 2 - positionOnCompleteGradient;
@ -121,19 +117,8 @@ namespace SixLabors.ImageSharp.Processing
} }
else else
{ {
var fromAsVector = from.Color.ToVector4();
var toAsVector = to.Color.ToVector4();
float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio); float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio);
return new Color(Vector4.Lerp((Vector4)from.Color, (Vector4)to.Color, onLocalGradient)).ToPixel<TPixel>();
// TODO: this should be changeble for different gradienting functions
Vector4 result = PorterDuffFunctions.NormalSrcOver(
fromAsVector,
toAsVector,
onLocalGradient);
TPixel resultColor = default;
resultColor.FromVector4(result);
return resultColor;
} }
} }
} }
@ -142,8 +127,8 @@ namespace SixLabors.ImageSharp.Processing
/// calculates the position on the gradient for a given point. /// calculates the position on the gradient for a given point.
/// This method is abstract as it's content depends on the shape of the gradient. /// This method is abstract as it's content depends on the shape of the gradient.
/// </summary> /// </summary>
/// <param name="x">The x coordinate of the point</param> /// <param name="x">The x-coordinate of the point.</param>
/// <param name="y">The y coordinate of the point</param> /// <param name="y">The y-coordinate of the point.</param>
/// <returns> /// <returns>
/// The position the given point has on the gradient. /// The position the given point has on the gradient.
/// The position is not bound to the [0..1] interval. /// The position is not bound to the [0..1] interval.
@ -176,4 +161,4 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
} }
} }

16
src/ImageSharp.Drawing/Processing/IBrush.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -19,20 +19,22 @@ namespace SixLabors.ImageSharp.Processing
/// Creates the applicator for this brush. /// Creates the applicator for this brush.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam> /// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The graphic options.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="region">The region the brush will be applied to.</param> /// <param name="region">The region the brush will be applied to.</param>
/// <param name="options">The graphic options</param>
/// <returns> /// <returns>
/// The brush applicator for this brush /// The <see cref="BrushApplicator{TPixel}"/> for this brush.
/// </returns> /// </returns>
/// <remarks> /// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would usually be the /// 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 /// bounding box of the shape not necessarily the bounds of the whole image.
/// </remarks> /// </remarks>
BrushApplicator<TPixel> CreateApplicator<TPixel>( BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region)
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>; where TPixel : struct, IPixel<TPixel>;
} }
} }

57
src/ImageSharp.Drawing/Processing/ImageBrush.cs

@ -1,11 +1,10 @@
// 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 SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -32,19 +31,20 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>( public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region)
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.image is Image<TPixel> specificImage) if (this.image is Image<TPixel> specificImage)
{ {
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, false); return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, region, false);
} }
specificImage = this.image.CloneAs<TPixel>(); specificImage = this.image.CloneAs<TPixel>();
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, true); return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, region, true);
} }
/// <summary> /// <summary>
@ -79,21 +79,25 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
private readonly int offsetX; private readonly int offsetX;
private bool isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator{TPixel}"/> class. /// Initializes a new instance of the <see cref="ImageBrushApplicator{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The graphics options.</param>
/// <param name="target">The target image.</param> /// <param name="target">The target image.</param>
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
/// <param name="region">The region.</param> /// <param name="region">The region.</param>
/// <param name="options">The options</param>
/// <param name="shouldDisposeImage">Whether to dispose the image on disposal of the applicator.</param> /// <param name="shouldDisposeImage">Whether to dispose the image on disposal of the applicator.</param>
public ImageBrushApplicator( public ImageBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> target, ImageFrame<TPixel> target,
Image<TPixel> image, Image<TPixel> image,
RectangleF region, RectangleF region,
GraphicsOptions options,
bool shouldDisposeImage) bool shouldDisposeImage)
: base(target, options) : base(configuration, options, target)
{ {
this.sourceImage = image; this.sourceImage = image;
this.sourceFrame = image.Frames.RootFrame; this.sourceFrame = image.Frames.RootFrame;
@ -104,14 +108,7 @@ namespace SixLabors.ImageSharp.Processing
this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0); this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0);
} }
/// <summary> /// <inheritdoc/>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] internal override TPixel this[int x, int y]
{ {
get get
@ -123,14 +120,21 @@ namespace SixLabors.ImageSharp.Processing
} }
/// <inheritdoc /> /// <inheritdoc />
public override void Dispose() protected override void Dispose(bool disposing)
{ {
if (this.shouldDisposeImage) if (this.isDisposed)
{
return;
}
if (disposing && this.shouldDisposeImage)
{ {
this.sourceImage?.Dispose(); this.sourceImage?.Dispose();
this.sourceImage = null;
this.sourceFrame = null;
} }
this.sourceImage = null;
this.sourceFrame = null;
this.isDisposed = true;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -140,8 +144,8 @@ namespace SixLabors.ImageSharp.Processing
using (IMemoryOwner<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length)) using (IMemoryOwner<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length)) using (IMemoryOwner<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length))
{ {
Span<float> amountSpan = amountBuffer.GetSpan(); Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.GetSpan(); Span<TPixel> overlaySpan = overlay.Memory.Span;
int sourceY = (y - this.offsetY) % this.yLength; int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX; int offsetX = x - this.offsetX;
@ -152,13 +156,12 @@ namespace SixLabors.ImageSharp.Processing
amountSpan[i] = scanline[i] * this.Options.BlendPercentage; amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int sourceX = (i + offsetX) % this.xLength; int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX]; overlaySpan[i] = sourceRow[sourceX];
overlaySpan[i] = pixel;
} }
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend( this.Blender.Blend(
this.sourceFrame.Configuration, this.Configuration,
destinationRow, destinationRow,
destinationRow, destinationRow,
overlaySpan, overlaySpan,
@ -167,4 +170,4 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
} }
} }

41
src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs

@ -1,4 +1,4 @@
// 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;
@ -39,16 +39,18 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>( public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region) =>
GraphicsOptions options) =>
new LinearGradientBrushApplicator<TPixel>( new LinearGradientBrushApplicator<TPixel>(
configuration,
options,
source, source,
this.p1, this.p1,
this.p2, this.p2,
this.ColorStops, this.ColorStops,
this.RepetitionMode, this.RepetitionMode);
options);
/// <summary> /// <summary>
/// The linear gradient brush applicator. /// The linear gradient brush applicator.
@ -93,20 +95,22 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator{TPixel}" /> class. /// Initializes a new instance of the <see cref="LinearGradientBrushApplicator{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="source">The source</param> /// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="start">start point of the gradient</param> /// <param name="options">The graphics options.</param>
/// <param name="end">end point of the gradient</param> /// <param name="source">The source image.</param>
/// <param name="colorStops">tuple list of colors and their respective position between 0 and 1 on the line</param> /// <param name="start">The start point of the gradient.</param>
/// <param name="repetitionMode">defines how the gradient colors are repeated.</param> /// <param name="end">The end point of the gradient.</param>
/// <param name="options">the graphics options</param> /// <param name="colorStops">A tuple list of colors and their respective position between 0 and 1 on the line.</param>
/// <param name="repetitionMode">Defines how the gradient colors are repeated.</param>
public LinearGradientBrushApplicator( public LinearGradientBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
PointF start, PointF start,
PointF end, PointF end,
ColorStop[] colorStops, ColorStop[] colorStops,
GradientRepetitionMode repetitionMode, GradientRepetitionMode repetitionMode)
GraphicsOptions options) : base(configuration, options, source, colorStops, repetitionMode)
: base(source, options, colorStops, repetitionMode)
{ {
this.start = start; this.start = start;
this.end = end; this.end = end;
@ -148,14 +152,9 @@ namespace SixLabors.ImageSharp.Processing
float distance = MathF.Sqrt(MathF.Pow(x4 - this.start.X, 2) + MathF.Pow(y4 - this.start.Y, 2)); float distance = MathF.Sqrt(MathF.Pow(x4 - this.start.X, 2) + MathF.Pow(y4 - this.start.Y, 2));
// get and return ratio // get and return ratio
float ratio = distance / this.length; return distance / this.length;
return ratio;
} }
} }
public override void Dispose()
{
}
} }
} }
} }

34
src/ImageSharp.Drawing/Processing/PathGradientBrush.cs

@ -83,12 +83,13 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>( public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region)
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return new PathGradientBrushApplicator<TPixel>(source, this.edges, this.centerColor, options); return new PathGradientBrushApplicator<TPixel>(configuration, options, source, this.edges, this.centerColor);
} }
private static Color CalculateCenterColor(Color[] colors) private static Color CalculateCenterColor(Color[] colors)
@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing
"One or more color is needed to construct a path gradient brush."); "One or more color is needed to construct a path gradient brush.");
} }
return new Color(colors.Select(c => c.ToVector4()).Aggregate((p1, p2) => p1 + p2) / colors.Length); return new Color(colors.Select(c => (Vector4)c).Aggregate((p1, p2) => p1 + p2) / colors.Length);
} }
private static float DistanceBetween(PointF p1, PointF p2) => ((Vector2)(p2 - p1)).Length(); private static float DistanceBetween(PointF p1, PointF p2) => ((Vector2)(p2 - p1)).Length();
@ -141,10 +142,10 @@ namespace SixLabors.ImageSharp.Processing
Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray(); Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray();
this.Start = points.First(); this.Start = points.First();
this.StartColor = startColor.ToVector4(); this.StartColor = (Vector4)startColor;
this.End = points.Last(); this.End = points.Last();
this.EndColor = endColor.ToVector4(); this.EndColor = (Vector4)endColor;
this.length = DistanceBetween(this.End, this.Start); this.length = DistanceBetween(this.End, this.Start);
this.buffer = new PointF[this.path.MaxIntersections]; this.buffer = new PointF[this.path.MaxIntersections];
@ -199,23 +200,25 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PathGradientBrushApplicator{TPixel}"/> class. /// Initializes a new instance of the <see cref="PathGradientBrushApplicator{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The graphics options.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="edges">Edges of the polygon.</param> /// <param name="edges">Edges of the polygon.</param>
/// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param> /// <param name="centerColor">Color at the center of the gradient area to which the other colors converge.</param>
/// <param name="options">The options.</param>
public PathGradientBrushApplicator( public PathGradientBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
IList<Edge> edges, IList<Edge> edges,
Color centerColor, Color centerColor)
GraphicsOptions options) : base(configuration, options, source)
: base(source, options)
{ {
this.edges = edges; this.edges = edges;
PointF[] points = edges.Select(s => s.Start).ToArray(); PointF[] points = edges.Select(s => s.Start).ToArray();
this.center = points.Aggregate((p1, p2) => p1 + p2) / edges.Count; this.center = points.Aggregate((p1, p2) => p1 + p2) / edges.Count;
this.centerColor = centerColor.ToVector4(); this.centerColor = (Vector4)centerColor;
this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max(); this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max();
} }
@ -232,7 +235,7 @@ namespace SixLabors.ImageSharp.Processing
return new Color(this.centerColor).ToPixel<TPixel>(); return new Color(this.centerColor).ToPixel<TPixel>();
} }
Vector2 direction = Vector2.Normalize(point - this.center); var direction = Vector2.Normalize(point - this.center);
PointF end = point + (PointF)(direction * this.maxDistance); PointF end = point + (PointF)(direction * this.maxDistance);
@ -250,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing
float length = DistanceBetween(intersection, this.center); float length = DistanceBetween(intersection, this.center);
float ratio = length > 0 ? DistanceBetween(intersection, point) / length : 0; float ratio = length > 0 ? DistanceBetween(intersection, point) / length : 0;
Vector4 color = Vector4.Lerp(edgeColor, this.centerColor, ratio); var color = Vector4.Lerp(edgeColor, this.centerColor, ratio);
return new Color(color).ToPixel<TPixel>(); return new Color(color).ToPixel<TPixel>();
} }
@ -277,11 +280,6 @@ namespace SixLabors.ImageSharp.Processing
return closest; return closest;
} }
/// <inheritdoc />
public override void Dispose()
{
}
} }
} }
} }

56
src/ImageSharp.Drawing/Processing/PatternBrush.cs

@ -1,4 +1,4 @@
// 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;
@ -61,8 +61,8 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix<bool> pattern) internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix<bool> pattern)
{ {
var foreColorVector = foreColor.ToVector4(); var foreColorVector = (Vector4)foreColor;
var backColorVector = backColor.ToVector4(); var backColorVector = (Vector4)backColor;
this.pattern = new DenseMatrix<Color>(pattern.Columns, pattern.Rows); this.pattern = new DenseMatrix<Color>(pattern.Columns, pattern.Rows);
this.patternVector = new DenseMatrix<Vector4>(pattern.Columns, pattern.Rows); this.patternVector = new DenseMatrix<Vector4>(pattern.Columns, pattern.Rows);
for (int i = 0; i < pattern.Data.Length; i++) for (int i = 0; i < pattern.Data.Length; i++)
@ -92,14 +92,16 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>( public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region)
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> => where TPixel : struct, IPixel<TPixel> =>
new PatternBrushApplicator<TPixel>( new PatternBrushApplicator<TPixel>(
configuration,
options,
source, source,
this.pattern.ToPixelMatrix<TPixel>(source.Configuration), this.pattern.ToPixelMatrix<TPixel>(configuration));
options);
/// <summary> /// <summary>
/// The pattern brush applicator. /// The pattern brush applicator.
@ -115,41 +117,33 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator{TPixel}" /> class. /// Initializes a new instance of the <see cref="PatternBrushApplicator{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The graphics options.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="options">The options</param> public PatternBrushApplicator(
public PatternBrushApplicator(ImageFrame<TPixel> source, in DenseMatrix<TPixel> pattern, GraphicsOptions options) Configuration configuration,
: base(source, options) GraphicsOptions options,
ImageFrame<TPixel> source,
in DenseMatrix<TPixel> pattern)
: base(configuration, options, source)
{ {
this.pattern = pattern; this.pattern = pattern;
} }
/// <summary> /// <inheritdoc/>
/// Gets the color for a single pixel.
/// </summary>#
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The Color.
/// </returns>
internal override TPixel this[int x, int y] internal override TPixel this[int x, int y]
{ {
get get
{ {
x = x % this.pattern.Columns; x %= this.pattern.Columns;
y = y % this.pattern.Rows; y %= this.pattern.Rows;
// 2d array index at row/column // 2d array index at row/column
return this.pattern[y, x]; return this.pattern[y, x];
} }
} }
/// <inheritdoc />
public override void Dispose()
{
// noop
}
/// <inheritdoc /> /// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
@ -159,12 +153,12 @@ namespace SixLabors.ImageSharp.Processing
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length)) using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length)) using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{ {
Span<float> amountSpan = amountBuffer.GetSpan(); Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.GetSpan(); Span<TPixel> overlaySpan = overlay.Memory.Span;
for (int i = 0; i < scanline.Length; i++) for (int i = 0; i < scanline.Length; i++)
{ {
amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); amountSpan[i] = NumberUtils.ClampFloat(scanline[i] * this.Options.BlendPercentage, 0, 1F);
int patternX = (x + i) % this.pattern.Columns; int patternX = (x + i) % this.pattern.Columns;
overlaySpan[i] = this.pattern[patternY, patternX]; overlaySpan[i] = this.pattern[patternY, patternX];
@ -172,7 +166,7 @@ namespace SixLabors.ImageSharp.Processing
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend( this.Blender.Blend(
this.Target.Configuration, this.Configuration,
destinationRow, destinationRow,
destinationRow, destinationRow,
overlaySpan, overlaySpan,
@ -181,4 +175,4 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
} }
} }

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

@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FillProcessor"/> class. /// Initializes a new instance of the <see cref="FillProcessor"/> class.
/// </summary> /// </summary>
/// <param name="brush">The brush to use for filling.</param>
/// <param name="options">The <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.</param> /// <param name="options">The <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.</param>
public FillProcessor(IBrush brush, GraphicsOptions options) /// <param name="brush">The brush to use for filling.</param>
public FillProcessor(GraphicsOptions options, IBrush brush)
{ {
this.Brush = brush; this.Brush = brush;
this.Options = options; this.Options = options;

13
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX; int width = maxX - minX;
Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
IBrush brush = this.definition.Brush; IBrush brush = this.definition.Brush;
GraphicsOptions options = this.definition.Options; GraphicsOptions options = this.definition.Options;
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration) ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration)
.MultiplyMinimumPixelsPerTask(4); .MultiplyMinimumPixelsPerTask(4);
var colorPixel = solidBrush.Color.ToPixel<TPixel>(); TPixel colorPixel = solidBrush.Color.ToPixel<TPixel>();
ParallelHelper.IterateRows( ParallelHelper.IterateRows(
workingRect, workingRect,
@ -84,11 +84,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width)) using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = brush.CreateApplicator( using (BrushApplicator<TPixel> applicator = brush.CreateApplicator(
configuration,
options,
source, source,
sourceRectangle, sourceRectangle))
options))
{ {
amount.GetSpan().Fill(1f); amount.Memory.Span.Fill(1f);
ParallelHelper.IterateRows( ParallelHelper.IterateRows(
workingRect, workingRect,
@ -100,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
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.Memory.Span, offsetX, offsetY);
} }
}); });
} }

4
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -16,10 +16,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FillRegionProcessor" /> class. /// Initializes a new instance of the <see cref="FillRegionProcessor" /> class.
/// </summary> /// </summary>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The details how to fill the region of interest.</param> /// <param name="brush">The details how to fill the region of interest.</param>
/// <param name="region">The region of interest to be filled.</param> /// <param name="region">The region of interest to be filled.</param>
/// <param name="options">The configuration options.</param> public FillRegionProcessor(GraphicsOptions options, IBrush brush, Region region)
public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options)
{ {
this.Region = region; this.Region = region;
this.Brush = brush; this.Brush = brush;

6
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs

@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
} }
} }
using (BrushApplicator<TPixel> applicator = brush.CreateApplicator(source, rect, options)) using (BrushApplicator<TPixel> applicator = brush.CreateApplicator(configuration, options, source, rect))
{ {
int scanlineWidth = maxX - minX; int scanlineWidth = maxX - minX;
using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections)) using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
@ -81,8 +81,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
float subpixelFraction = 1f / subpixelCount; float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan(); Span<float> buffer = bBuffer.Memory.Span;
Span<float> scanline = bScanline.GetSpan(); Span<float> scanline = bScanline.Memory.Span;
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush);
TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel<TPixel>() : default; TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel<TPixel>() : default;

8
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
VerticalAlignment = this.Options.VerticalAlignment VerticalAlignment = this.Options.VerticalAlignment
}; };
this.textRenderer = new CachingGlyphRenderer(this.Source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); this.textRenderer = new CachingGlyphRenderer(this.Configuration.MemoryAllocator, this.Text.Length, this.Pen, this.Brush != null);
this.textRenderer.Options = (GraphicsOptions)this.Options; this.textRenderer.Options = (GraphicsOptions)this.Options;
var renderer = new TextRenderer(this.textRenderer); var renderer = new TextRenderer(this.textRenderer);
renderer.RenderText(this.Text, style); renderer.RenderText(this.Text, style);
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{ {
if (operations?.Count > 0) if (operations?.Count > 0)
{ {
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, this.SourceRectangle, this.textRenderer.Options)) using (BrushApplicator<TPixel> app = brush.CreateApplicator(this.Configuration, this.textRenderer.Options, source, this.SourceRectangle))
{ {
foreach (DrawingOperation operation in operations) foreach (DrawingOperation operation in operations)
{ {
@ -326,6 +326,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{ {
float subpixelFraction = 1f / subpixelCount; float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<PointF> intersectionSpan = rowIntersectionBuffer.Memory.Span;
Span<float> buffer = bufferBacking.Memory.Span;
for (int y = 0; y <= size.Height; y++) for (int y = 0; y <= size.Height; y++)
{ {
@ -337,8 +339,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{ {
var start = new PointF(path.Bounds.Left - 1, subPixel); var start = new PointF(path.Bounds.Left - 1, subPixel);
var end = new PointF(path.Bounds.Right + 1, subPixel); var end = new PointF(path.Bounds.Right + 1, subPixel);
Span<PointF> intersectionSpan = rowIntersectionBuffer.GetSpan();
Span<float> buffer = bufferBacking.GetSpan();
int pointsFound = path.FindIntersections(start, end, intersectionSpan); int pointsFound = path.FindIntersections(start, end, intersectionSpan);
if (pointsFound == 0) if (pointsFound == 0)

30
src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs

@ -1,4 +1,4 @@
// 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;
@ -9,7 +9,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
{ {
/// <summary> /// <summary>
/// A Circular Gradient Brush, defined by center point and radius. /// A radial gradient brush, defined by center point and radius.
/// </summary> /// </summary>
public sealed class RadialGradientBrush : GradientBrush public sealed class RadialGradientBrush : GradientBrush
{ {
@ -35,12 +35,14 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>( public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region) =>
GraphicsOptions options) =>
new RadialGradientBrushApplicator<TPixel>( new RadialGradientBrushApplicator<TPixel>(
source, configuration,
options, options,
source,
this.center, this.center,
this.radius, this.radius,
this.ColorStops, this.ColorStops,
@ -57,30 +59,27 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class. /// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="target">The target image</param> /// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The options.</param> /// <param name="options">The graphics options.</param>
/// <param name="target">The target image.</param>
/// <param name="center">Center point of the gradient.</param> /// <param name="center">Center point of the gradient.</param>
/// <param name="radius">Radius of the gradient.</param> /// <param name="radius">Radius of the gradient.</param>
/// <param name="colorStops">Definition of colors.</param> /// <param name="colorStops">Definition of colors.</param>
/// <param name="repetitionMode">How the colors are repeated beyond the first gradient.</param> /// <param name="repetitionMode">How the colors are repeated beyond the first gradient.</param>
public RadialGradientBrushApplicator( public RadialGradientBrushApplicator(
ImageFrame<TPixel> target, Configuration configuration,
GraphicsOptions options, GraphicsOptions options,
ImageFrame<TPixel> target,
PointF center, PointF center,
float radius, float radius,
ColorStop[] colorStops, ColorStop[] colorStops,
GradientRepetitionMode repetitionMode) GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode) : base(configuration, options, target, colorStops, repetitionMode)
{ {
this.center = center; this.center = center;
this.radius = radius; this.radius = radius;
} }
/// <inheritdoc cref="Dispose" />
public override void Dispose()
{
}
/// <summary> /// <summary>
/// As this is a circular gradient, the position on the gradient is based on /// As this is a circular gradient, the position on the gradient is based on
/// the distance of the point to the center. /// the distance of the point to the center.
@ -90,6 +89,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>the position on the color gradient.</returns> /// <returns>the position on the color gradient.</returns>
protected override float PositionOnGradient(float x, float y) protected override float PositionOnGradient(float x, float y)
{ {
// TODO: Can this not use Vector2 distance?
float distance = MathF.Sqrt(MathF.Pow(this.center.X - x, 2) + MathF.Pow(this.center.Y - y, 2)); float distance = MathF.Sqrt(MathF.Pow(this.center.X - x, 2) + MathF.Pow(this.center.Y - y, 2));
return distance / this.radius; return distance / this.radius;
} }
@ -101,4 +101,4 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
} }
} }

57
src/ImageSharp.Drawing/Processing/RecolorBrush.cs

@ -1,11 +1,10 @@
// 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.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -38,9 +37,6 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Gets the source color. /// Gets the source color.
/// </summary> /// </summary>
/// <value>
/// The color of the source.
/// </value>
public Color SourceColor { get; } public Color SourceColor { get; }
/// <summary> /// <summary>
@ -50,17 +46,19 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc /> /// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>( public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
RectangleF region, RectangleF region)
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return new RecolorBrushApplicator<TPixel>( return new RecolorBrushApplicator<TPixel>(
configuration,
options,
source, source,
this.SourceColor.ToPixel<TPixel>(), this.SourceColor.ToPixel<TPixel>(),
this.TargetColor.ToPixel<TPixel>(), this.TargetColor.ToPixel<TPixel>(),
this.Threshold, this.Threshold);
options);
} }
/// <summary> /// <summary>
@ -74,11 +72,6 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
private readonly Vector4 sourceColor; private readonly Vector4 sourceColor;
/// <summary>
/// The target color.
/// </summary>
private readonly Vector4 targetColor;
/// <summary> /// <summary>
/// The threshold. /// The threshold.
/// </summary> /// </summary>
@ -89,16 +82,22 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RecolorBrushApplicator{TPixel}" /> class. /// Initializes a new instance of the <see cref="RecolorBrushApplicator{TPixel}" /> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The options</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="sourceColor">Color of the source.</param> /// <param name="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param> /// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param> /// <param name="threshold">The threshold .</param>
/// <param name="options">The options</param> public RecolorBrushApplicator(
public RecolorBrushApplicator(ImageFrame<TPixel> source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) Configuration configuration,
: base(source, options) GraphicsOptions options,
ImageFrame<TPixel> source,
TPixel sourceColor,
TPixel targetColor,
float threshold)
: base(configuration, options, source)
{ {
this.sourceColor = sourceColor.ToVector4(); this.sourceColor = sourceColor.ToVector4();
this.targetColor = targetColor.ToVector4();
this.targetColorPixel = targetColor; this.targetColorPixel = targetColor;
// Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :) // Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :)
@ -109,14 +108,7 @@ namespace SixLabors.ImageSharp.Processing
this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold;
} }
/// <summary> /// <inheritdoc />
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] internal override TPixel this[int x, int y]
{ {
get get
@ -138,11 +130,6 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc /> /// <inheritdoc />
internal override void Apply(Span<float> scanline, int x, int y) internal override void Apply(Span<float> scanline, int x, int y)
{ {
@ -151,8 +138,8 @@ namespace SixLabors.ImageSharp.Processing
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length)) using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length)) using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{ {
Span<float> amountSpan = amountBuffer.GetSpan(); Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.GetSpan(); Span<TPixel> overlaySpan = overlay.Memory.Span;
for (int i = 0; i < scanline.Length; i++) for (int i = 0; i < scanline.Length; i++)
{ {
@ -167,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend( this.Blender.Blend(
this.Target.Configuration, this.Configuration,
destinationRow, destinationRow,
destinationRow, destinationRow,
overlaySpan, overlaySpan,
@ -176,4 +163,4 @@ namespace SixLabors.ImageSharp.Processing
} }
} }
} }
} }

76
src/ImageSharp.Drawing/Processing/SolidBrush.cs

@ -1,11 +1,10 @@
// 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 SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -17,33 +16,29 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
public class SolidBrush : IBrush public class SolidBrush : IBrush
{ {
/// <summary>
/// The color to paint.
/// </summary>
private readonly Color color;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SolidBrush"/> class. /// Initializes a new instance of the <see cref="SolidBrush"/> class.
/// </summary> /// </summary>
/// <param name="color">The color.</param> /// <param name="color">The color.</param>
public SolidBrush(Color color) public SolidBrush(Color color)
{ {
this.color = color; this.Color = color;
} }
/// <summary> /// <summary>
/// Gets the color. /// Gets the color.
/// </summary> /// </summary>
/// <value> public Color Color { get; }
/// The color.
/// </value>
public Color Color => this.color;
/// <inheritdoc /> /// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options) public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return new SolidBrushApplicator<TPixel>(source, this.color.ToPixel<TPixel>(), options); return new SolidBrushApplicator<TPixel>(configuration, options, source, this.Color.ToPixel<TPixel>());
} }
/// <summary> /// <summary>
@ -52,38 +47,49 @@ namespace SixLabors.ImageSharp.Processing
private class SolidBrushApplicator<TPixel> : BrushApplicator<TPixel> private class SolidBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private bool isDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator{TPixel}"/> class. /// Initializes a new instance of the <see cref="SolidBrushApplicator{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration instance to use when performing operations.</param>
/// <param name="options">The graphics options.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="color">The color.</param> /// <param name="color">The color.</param>
/// <param name="options">The options</param> public SolidBrushApplicator(
public SolidBrushApplicator(ImageFrame<TPixel> source, TPixel color, GraphicsOptions options) Configuration configuration,
: base(source, options) GraphicsOptions options,
ImageFrame<TPixel> source,
TPixel color)
: base(configuration, options, source)
{ {
this.Colors = source.MemoryAllocator.Allocate<TPixel>(source.Width); this.Colors = source.MemoryAllocator.Allocate<TPixel>(source.Width);
this.Colors.GetSpan().Fill(color); this.Colors.Memory.Span.Fill(color);
} }
/// <summary> /// <summary>
/// Gets the colors. /// Gets the colors.
/// </summary> /// </summary>
protected IMemoryOwner<TPixel> Colors { get; } protected IMemoryOwner<TPixel> Colors { get; private set; }
/// <summary> /// <inheritdoc/>
/// Gets the color for a single pixel. internal override TPixel this[int x, int y] => this.Colors.Memory.Span[x];
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
internal override TPixel this[int x, int y] => this.Colors.GetSpan()[x];
/// <inheritdoc /> /// <inheritdoc />
public override void Dispose() protected override void Dispose(bool disposing)
{ {
this.Colors.Dispose(); if (this.isDisposed)
{
return;
}
if (disposing)
{
this.Colors.Dispose();
}
this.Colors = null;
this.isDisposed = true;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -102,17 +108,17 @@ namespace SixLabors.ImageSharp.Processing
} }
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
Configuration configuration = this.Target.Configuration; Configuration configuration = this.Configuration;
if (this.Options.BlendPercentage == 1f) if (this.Options.BlendPercentage == 1f)
{ {
this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.Memory.Span, scanline);
} }
else else
{ {
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length)) using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
{ {
Span<float> amountSpan = amountBuffer.GetSpan(); Span<float> amountSpan = amountBuffer.Memory.Span;
for (int i = 0; i < scanline.Length; i++) for (int i = 0; i < scanline.Length; i++)
{ {
@ -123,11 +129,11 @@ namespace SixLabors.ImageSharp.Processing
configuration, configuration,
destinationRow, destinationRow,
destinationRow, destinationRow,
this.Colors.GetSpan(), this.Colors.Memory.Span,
amountSpan); amountSpan);
} }
} }
} }
} }
} }
} }

180
src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs

@ -1,4 +1,4 @@
// 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 SixLabors.Fonts; using SixLabors.Fonts;
@ -9,120 +9,169 @@ namespace SixLabors.ImageSharp.Processing
/// <summary> /// <summary>
/// Options for influencing the drawing functions. /// Options for influencing the drawing functions.
/// </summary> /// </summary>
public struct TextGraphicsOptions public class TextGraphicsOptions : IDeepCloneable<TextGraphicsOptions>
{ {
private const int DefaultTextDpi = 72; private int antialiasSubpixelDepth = 16;
private float blendPercentage = 1F;
private float tabWidth = 4F;
private float dpiX = 72F;
private float dpiY = 72F;
/// <summary> /// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>. /// Initializes a new instance of the <see cref="TextGraphicsOptions"/> class.
/// </summary> /// </summary>
public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); public TextGraphicsOptions()
{
private float? blendPercentage; }
private int? antialiasSubpixelDepth;
private bool? antialias;
private bool? applyKerning;
private float? tabWidth;
private float? dpiX;
private float? dpiY;
private HorizontalAlignment? horizontalAlignment;
private VerticalAlignment? verticalAlignment;
/// <summary> private TextGraphicsOptions(TextGraphicsOptions source)
/// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public TextGraphicsOptions(bool enableAntialiasing)
{ {
this.applyKerning = true; this.AlphaCompositionMode = source.AlphaCompositionMode;
this.tabWidth = 4; this.Antialias = source.Antialias;
this.WrapTextWidth = 0; this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth;
this.horizontalAlignment = HorizontalAlignment.Left; this.ApplyKerning = source.ApplyKerning;
this.verticalAlignment = VerticalAlignment.Top; this.BlendPercentage = source.BlendPercentage;
this.ColorBlendingMode = source.ColorBlendingMode;
this.antialiasSubpixelDepth = 16; this.DpiX = source.DpiX;
this.ColorBlendingMode = PixelColorBlendingMode.Normal; this.DpiY = source.DpiY;
this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.HorizontalAlignment = source.HorizontalAlignment;
this.blendPercentage = 1; this.TabWidth = source.TabWidth;
this.antialias = enableAntialiasing; this.WrapTextWidth = source.WrapTextWidth;
this.dpiX = DefaultTextDpi; this.VerticalAlignment = source.VerticalAlignment;
this.dpiY = DefaultTextDpi;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied. /// Gets or sets a value indicating whether antialiasing should be applied.
/// Defaults to true.
/// </summary> /// </summary>
public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } public bool Antialias { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary> /// </summary>
public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } public int AntialiasSubpixelDepth
{
get
{
return this.antialiasSubpixelDepth;
}
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth));
this.antialiasSubpixelDepth = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// Gets or sets a value indicating the blending percentage to apply to the drawing operation.
/// </summary> /// </summary>
public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } public float BlendPercentage
{
get
{
return this.blendPercentage;
}
// In the future we could expose a PixelBlender<TPixel> directly on here set
// or some forms of PixelBlender factory for each pixel type. Will need {
// some API thought post V1. Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage));
this.blendPercentage = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation.
/// Defaults to <see cref= "PixelColorBlendingMode.Normal" />.
/// </summary> /// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; set; } public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal;
/// <summary> /// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// Defaults to <see cref= "PixelAlphaCompositionMode.SrcOver" />.
/// </summary> /// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled. /// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
/// Defaults to true;
/// </summary> /// </summary>
public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } public bool ApplyKerning { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating the number of space widths a tab should lock to. /// Gets or sets a value indicating the number of space widths a tab should lock to.
/// Defaults to 4.
/// </summary> /// </summary>
public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } public float TabWidth
{
get
{
return this.tabWidth;
}
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.TabWidth));
this.tabWidth = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. /// Gets or sets a value, if greater than 0, indicating the width at which text should wrap.
/// Defaults to 0.
/// </summary> /// </summary>
public float WrapTextWidth { get; set; } public float WrapTextWidth { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the DPI to render text along the X axis. /// Gets or sets a value indicating the DPI (Dots Per Inch) to render text along the X axis.
/// Defaults to 72.
/// </summary> /// </summary>
public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } public float DpiX
{
get
{
return this.dpiX;
}
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.DpiX));
this.dpiX = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating the DPI to render text along the Y axis. /// Gets or sets a value indicating the DPI (Dots Per Inch) to render text along the Y axis.
/// Defaults to 72.
/// </summary> /// </summary>
public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } public float DpiY
{
get
{
return this.dpiY;
}
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.DpiY));
this.dpiY = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space. /// Gets or sets a value indicating how to align the text relative to the rendering space.
/// If <see cref="WrapTextWidth"/> is greater than zero it will align relative to the space /// If <see cref="WrapTextWidth"/> is greater than zero it will align relative to the space
/// defined by the location and width, if <see cref="WrapTextWidth"/> equals zero, and thus /// defined by the location and width, if <see cref="WrapTextWidth"/> equals zero, and thus
/// wrapping disabled, then the alignment is relative to the drawing location. /// wrapping disabled, then the alignment is relative to the drawing location.
/// Defaults to <see cref="HorizontalAlignment.Left"/>.
/// </summary> /// </summary>
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Left;
/// <summary> /// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space. /// Gets or sets a value indicating how to align the text relative to the rendering space.
/// Defaults to <see cref="VerticalAlignment.Top"/>.
/// </summary> /// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Top;
/// <summary> /// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>. /// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.
@ -133,8 +182,9 @@ namespace SixLabors.ImageSharp.Processing
/// </returns> /// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options) public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{ {
return new TextGraphicsOptions(options.Antialias) return new TextGraphicsOptions()
{ {
Antialias = options.Antialias,
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage, blendPercentage = options.BlendPercentage,
ColorBlendingMode = options.ColorBlendingMode, ColorBlendingMode = options.ColorBlendingMode,
@ -151,13 +201,17 @@ namespace SixLabors.ImageSharp.Processing
/// </returns> /// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options) public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{ {
return new GraphicsOptions(options.Antialias) return new GraphicsOptions()
{ {
Antialias = options.Antialias,
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
ColorBlendingMode = options.ColorBlendingMode, ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode, AlphaCompositionMode = options.AlphaCompositionMode,
BlendPercentage = options.BlendPercentage BlendPercentage = options.BlendPercentage
}; };
} }
/// <inheritdoc/>
public TextGraphicsOptions DeepClone() => new TextGraphicsOptions(this);
} }
} }

29
src/ImageSharp.Drawing/Utils/NumberUtils.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Utility methods for numeric primitives.
/// </summary>
internal static class NumberUtils
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ClampFloat(float value, float min, float max)
{
if (value >= max)
{
return max;
}
if (value <= min)
{
return min;
}
return value;
}
}
}

2
src/ImageSharp.Drawing/Utils/QuickSort.cs

@ -1,4 +1,4 @@
// 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;

35
src/ImageSharp/Color/Color.cs

@ -133,8 +133,7 @@ namespace SixLabors.ImageSharp
public override string ToString() => this.ToHex(); public override string ToString() => this.ToHex();
/// <summary> /// <summary>
/// Converts the color instance to an <see cref="IPixel{TSelf}"/> /// Converts the color instance to a specified <see cref="IPixel{TSelf}"/> type.
/// implementation defined by <typeparamref name="TPixel"/>.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam> /// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <returns>The pixel value.</returns> /// <returns>The pixel value.</returns>
@ -147,6 +146,24 @@ namespace SixLabors.ImageSharp
return pixel; return pixel;
} }
/// <summary>
/// Bulk converts a span of <see cref="Color"/> to a span of a specified <see cref="IPixel{TSelf}"/> type.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void ToPixel<TPixel>(
Configuration configuration,
ReadOnlySpan<Color> source,
Span<TPixel> destination)
where TPixel : struct, IPixel<TPixel>
{
ReadOnlySpan<Rgba64> rgba64Span = MemoryMarshal.Cast<Color, Rgba64>(source);
PixelOperations<TPixel>.Instance.FromRgba64(configuration, rgba64Span, destination);
}
/// <inheritdoc /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Color other) public bool Equals(Color other)
@ -166,19 +183,5 @@ namespace SixLabors.ImageSharp
{ {
return this.data.PackedValue.GetHashCode(); return this.data.PackedValue.GetHashCode();
} }
/// <summary>
/// Bulk convert a span of <see cref="Color"/> to a span of a specified pixel type.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
internal static void ToPixel<TPixel>(
Configuration configuration,
ReadOnlySpan<Color> source,
Span<TPixel> destination)
where TPixel : struct, IPixel<TPixel>
{
ReadOnlySpan<Rgba64> rgba64Span = MemoryMarshal.Cast<Color, Rgba64>(source);
PixelOperations<TPixel>.Instance.FromRgba64(configuration, rgba64Span, destination);
}
} }
} }

178
src/ImageSharp/GraphicsOptions.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -8,170 +8,82 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Options for influencing the drawing functions. /// Options for influencing the drawing functions.
/// </summary> /// </summary>
public struct GraphicsOptions public class GraphicsOptions : IDeepCloneable<GraphicsOptions>
{ {
/// <summary> private int antialiasSubpixelDepth = 16;
/// Represents the default <see cref="GraphicsOptions"/>. private float blendPercentage = 1F;
/// </summary>
public static readonly GraphicsOptions Default = new GraphicsOptions(true);
private float? blendPercentage;
private int? antialiasSubpixelDepth;
private bool? antialias;
private PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing)
{
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct. /// Initializes a new instance of the <see cref="GraphicsOptions"/> class.
/// </summary> /// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> public GraphicsOptions()
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
/// <param name="blending">color blending mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
} }
/// <summary> private GraphicsOptions(GraphicsOptions source)
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
/// <param name="blending">color blending mode to apply to the drawing operation</param>
/// <param name="composition">alpha composition mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.AlphaCompositionMode = source.AlphaCompositionMode;
this.Antialias = source.Antialias;
this.colorBlendingMode = blending; this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth;
this.alphaCompositionMode = composition; this.BlendPercentage = source.BlendPercentage;
this.blendPercentage = opacity; this.ColorBlendingMode = source.ColorBlendingMode;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied. /// Gets or sets a value indicating whether antialiasing should be applied.
/// Defaults to true.
/// </summary> /// </summary>
public bool Antialias public bool Antialias { get; set; } = true;
{
get => this.antialias ?? true;
set => this.antialias = value;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// Defaults to 16.
/// </summary> /// </summary>
public int AntialiasSubpixelDepth public int AntialiasSubpixelDepth
{ {
get => this.antialiasSubpixelDepth ?? 16; get
set => this.antialiasSubpixelDepth = value; {
return this.antialiasSubpixelDepth;
}
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth));
this.antialiasSubpixelDepth = value;
}
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// Gets or sets a value between indicating the blending percentage to apply to the drawing operation.
/// Range 0..1; Defaults to 1.
/// </summary> /// </summary>
public float BlendPercentage public float BlendPercentage
{ {
get => (this.blendPercentage ?? 1).Clamp(0, 1); get
set => this.blendPercentage = value; {
} return this.blendPercentage;
}
// In the future we could expose a PixelBlender<TPixel> directly on here set
// or some forms of PixelBlender factory for each pixel type. Will need {
// some API thought post V1. Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage));
this.blendPercentage = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation /// Gets or sets a value indicating the color blending mode to apply to the drawing operation.
/// Defaults to <see cref="PixelColorBlendingMode.Normal"/>.
/// </summary> /// </summary>
public PixelColorBlendingMode ColorBlendingMode public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal;
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// Defaults to <see cref="PixelAlphaCompositionMode.SrcOver"/>.
/// </summary> /// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver;
{
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
}
/// <summary> /// <inheritdoc/>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. public GraphicsOptions DeepClone() => new GraphicsOptions(this);
/// </summary>
/// <param name="color">the color</param>
/// <returns>true if the color can be considered opaque</returns>
/// <remarks>
/// Blending and composition is an expensive operation, in some cases, like
/// filling with a solid color, the blending can be avoided by a plain color replacement.
/// This method can be useful for such processors to select the fast path.
/// </remarks>
internal bool IsOpaqueColorWithoutBlending(Color color)
{
if (this.ColorBlendingMode != PixelColorBlendingMode.Normal)
{
return false;
}
if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver &&
this.AlphaCompositionMode != PixelAlphaCompositionMode.Src)
{
return false;
}
if (this.BlendPercentage != 1f)
{
return false;
}
if (color.ToVector4().W != 1f)
{
return false;
}
return true;
}
} }
} }

27
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -1,4 +1,4 @@
// 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.Buffers; using System.Buffers;
@ -11,8 +11,18 @@ namespace SixLabors.ImageSharp.Memory
/// <summary> /// <summary>
/// Extension methods for <see cref="MemoryAllocator"/>. /// Extension methods for <see cref="MemoryAllocator"/>.
/// </summary> /// </summary>
internal static class MemoryAllocatorExtensions public static class MemoryAllocatorExtensions
{ {
/// <summary>
/// Allocates a buffer of value type objects interpreted as a 2D region
/// of <paramref name="width"/> x <paramref name="height"/> elements.
/// </summary>
/// <typeparam name="T">The type of buffer items to allocate.</typeparam>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The buffer width.</param>
/// <param name="height">The buffer heght.</param>
/// <param name="options">The allocation options.</param>
/// <returns>The <see cref="Buffer2D{T}"/>.</returns>
public static Buffer2D<T> Allocate2D<T>( public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator, this MemoryAllocator memoryAllocator,
int width, int width,
@ -26,6 +36,15 @@ namespace SixLabors.ImageSharp.Memory
return new Buffer2D<T>(memorySource, width, height); return new Buffer2D<T>(memorySource, width, height);
} }
/// <summary>
/// Allocates a buffer of value type objects interpreted as a 2D region
/// of <paramref name="size"/> width x <paramref name="size"/> height elements.
/// </summary>
/// <typeparam name="T">The type of buffer items to allocate.</typeparam>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="size">The buffer size.</param>
/// <param name="options">The allocation options.</param>
/// <returns>The <see cref="Buffer2D{T}"/>.</returns>
public static Buffer2D<T> Allocate2D<T>( public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator, this MemoryAllocator memoryAllocator,
Size size, Size size,
@ -41,7 +60,7 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="pixelSizeInBytes">The pixel size in bytes, eg. 3 for RGB</param> /// <param name="pixelSizeInBytes">The pixel size in bytes, eg. 3 for RGB</param>
/// <param name="paddingInBytes">The padding</param> /// <param name="paddingInBytes">The padding</param>
/// <returns>A <see cref="IManagedByteBuffer"/></returns> /// <returns>A <see cref="IManagedByteBuffer"/></returns>
public static IManagedByteBuffer AllocatePaddedPixelRowBuffer( internal static IManagedByteBuffer AllocatePaddedPixelRowBuffer(
this MemoryAllocator memoryAllocator, this MemoryAllocator memoryAllocator,
int width, int width,
int pixelSizeInBytes, int pixelSizeInBytes,
@ -51,4 +70,4 @@ namespace SixLabors.ImageSharp.Memory
return memoryAllocator.AllocateManagedByteBuffer(length); return memoryAllocator.AllocateManagedByteBuffer(length);
} }
} }
} }

8
src/ImageSharp/Primitives/DenseMatrix{T}.cs

@ -1,4 +1,4 @@
// 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;
@ -98,9 +98,9 @@ namespace SixLabors.ImageSharp.Primitives
} }
/// <summary> /// <summary>
/// Gets a Span wrapping the Data. /// Gets a span wrapping the <see cref="Data"/>.
/// </summary> /// </summary>
internal Span<T> Span => new Span<T>(this.Data); public Span<T> Span => new Span<T>(this.Data);
/// <summary> /// <summary>
/// Gets or sets the item at the specified position. /// Gets or sets the item at the specified position.
@ -222,4 +222,4 @@ namespace SixLabors.ImageSharp.Primitives
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() => this.Data.GetHashCode(); public override int GetHashCode() => this.Data.GetHashCode();
} }
} }

12
src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Overlays;
@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="color">The color to set as the background.</param> /// <param name="color">The color to set as the background.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) => public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) =>
BackgroundColor(source, GraphicsOptions.Default, color); BackgroundColor(source, new GraphicsOptions(), color);
/// <summary> /// <summary>
/// Replaces the background color of image with the given one. /// Replaces the background color of image with the given one.
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
Color color, Color color,
Rectangle rectangle) => Rectangle rectangle) =>
BackgroundColor(source, GraphicsOptions.Default, color, rectangle); BackgroundColor(source, new GraphicsOptions(), color, rectangle);
/// <summary> /// <summary>
/// Replaces the background color of image with the given one. /// Replaces the background color of image with the given one.
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
GraphicsOptions options, GraphicsOptions options,
Color color) => Color color) =>
source.ApplyProcessor(new BackgroundColorProcessor(color, options)); source.ApplyProcessor(new BackgroundColorProcessor(options, color));
/// <summary> /// <summary>
/// Replaces the background color of image with the given one. /// Replaces the background color of image with the given one.
@ -64,6 +64,6 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options, GraphicsOptions options,
Color color, Color color,
Rectangle rectangle) => Rectangle rectangle) =>
source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); source.ApplyProcessor(new BackgroundColorProcessor(options, color), rectangle);
} }
} }

18
src/ImageSharp/Processing/Extensions/GlowExtensions.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source) => public static IImageProcessingContext Glow(this IImageProcessingContext source) =>
Glow(source, GraphicsOptions.Default); Glow(source, new GraphicsOptions());
/// <summary> /// <summary>
/// Applies a radial glow effect to an image. /// Applies a radial glow effect to an image.
@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color) public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color)
{ {
return Glow(source, GraphicsOptions.Default, color); return Glow(source, new GraphicsOptions(), color);
} }
/// <summary> /// <summary>
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="radius">The the radius.</param> /// <param name="radius">The the radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) => public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) =>
Glow(source, GraphicsOptions.Default, radius); Glow(source, new GraphicsOptions(), radius);
/// <summary> /// <summary>
/// Applies a radial glow effect to an image. /// Applies a radial glow effect to an image.
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) => public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) =>
source.Glow(GraphicsOptions.Default, rectangle); source.Glow(new GraphicsOptions(), rectangle);
/// <summary> /// <summary>
/// Applies a radial glow effect to an image. /// Applies a radial glow effect to an image.
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing
Color color, Color color,
float radius, float radius,
Rectangle rectangle) => Rectangle rectangle) =>
source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); source.Glow(new GraphicsOptions(), color, ValueSize.Absolute(radius), rectangle);
/// <summary> /// <summary>
/// Applies a radial glow effect to an image. /// Applies a radial glow effect to an image.
@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Processing
Color color, Color color,
ValueSize radius, ValueSize radius,
Rectangle rectangle) => Rectangle rectangle) =>
source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); source.ApplyProcessor(new GlowProcessor(options, color, radius), rectangle);
/// <summary> /// <summary>
/// Applies a radial glow effect to an image. /// Applies a radial glow effect to an image.
@ -170,6 +170,6 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options, GraphicsOptions options,
Color color, Color color,
ValueSize radius) => ValueSize radius) =>
source.ApplyProcessor(new GlowProcessor(color, radius, options)); source.ApplyProcessor(new GlowProcessor(options, color, radius));
} }
} }

18
src/ImageSharp/Processing/Extensions/VignetteExtensions.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source) => public static IImageProcessingContext Vignette(this IImageProcessingContext source) =>
Vignette(source, GraphicsOptions.Default); Vignette(source, new GraphicsOptions());
/// <summary> /// <summary>
/// Applies a radial vignette effect to an image. /// Applies a radial vignette effect to an image.
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="color">The color to set as the vignette.</param> /// <param name="color">The color to set as the vignette.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) => public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) =>
Vignette(source, GraphicsOptions.Default, color); Vignette(source, new GraphicsOptions(), color);
/// <summary> /// <summary>
/// Applies a radial vignette effect to an image. /// Applies a radial vignette effect to an image.
@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source, this IImageProcessingContext source,
float radiusX, float radiusX,
float radiusY) => float radiusY) =>
Vignette(source, GraphicsOptions.Default, radiusX, radiusY); Vignette(source, new GraphicsOptions(), radiusX, radiusY);
/// <summary> /// <summary>
/// Applies a radial vignette effect to an image. /// Applies a radial vignette effect to an image.
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
/// </param> /// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) => public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) =>
Vignette(source, GraphicsOptions.Default, rectangle); Vignette(source, new GraphicsOptions(), rectangle);
/// <summary> /// <summary>
/// Applies a radial vignette effect to an image. /// Applies a radial vignette effect to an image.
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing
float radiusX, float radiusX,
float radiusY, float radiusY,
Rectangle rectangle) => Rectangle rectangle) =>
source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); source.Vignette(new GraphicsOptions(), color, radiusX, radiusY, rectangle);
/// <summary> /// <summary>
/// Applies a radial vignette effect to an image. /// Applies a radial vignette effect to an image.
@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Processing
ValueSize radiusX, ValueSize radiusX,
ValueSize radiusY, ValueSize radiusY,
Rectangle rectangle) => Rectangle rectangle) =>
source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY), rectangle);
private static IImageProcessingContext VignetteInternal( private static IImageProcessingContext VignetteInternal(
this IImageProcessingContext source, this IImageProcessingContext source,
@ -174,6 +174,6 @@ namespace SixLabors.ImageSharp.Processing
Color color, Color color,
ValueSize radiusX, ValueSize radiusX,
ValueSize radiusY) => ValueSize radiusY) =>
source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY));
} }
} }

4
src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs

@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor"/> class. /// Initializes a new instance of the <see cref="BackgroundColorProcessor"/> class.
/// </summary> /// </summary>
/// <param name="color">The <see cref="Color"/> to set the background color to.</param>
/// <param name="options">The options defining blending algorithm and amount.</param> /// <param name="options">The options defining blending algorithm and amount.</param>
public BackgroundColorProcessor(Color color, GraphicsOptions options) /// <param name="color">The <see cref="Color"/> to set the background color to.</param>
public BackgroundColorProcessor(GraphicsOptions options, Color color)
{ {
this.Color = color; this.Color = color;
this.GraphicsOptions = options; this.GraphicsOptions = options;

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

@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor" /> class. /// Initializes a new instance of the <see cref="GlowProcessor" /> class.
/// </summary> /// </summary>
/// <param name="color">The color or the glow.</param>
/// <param name="options">The options effecting blending and composition.</param> /// <param name="options">The options effecting blending and composition.</param>
public GlowProcessor(Color color, GraphicsOptions options) /// <param name="color">The color or the glow.</param>
: this(color, 0, options) public GlowProcessor(GraphicsOptions options, Color color)
: this(options, color, 0)
{ {
} }
@ -37,17 +37,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <param name="color">The color or the glow.</param> /// <param name="color">The color or the glow.</param>
/// <param name="radius">The radius of the glow.</param> /// <param name="radius">The radius of the glow.</param>
internal GlowProcessor(Color color, ValueSize radius) internal GlowProcessor(Color color, ValueSize radius)
: this(color, radius, GraphicsOptions.Default) : this(new GraphicsOptions(), color, radius)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor" /> class. /// Initializes a new instance of the <see cref="GlowProcessor" /> class.
/// </summary> /// </summary>
/// <param name="options">The options effecting blending and composition.</param>
/// <param name="color">The color or the glow.</param> /// <param name="color">The color or the glow.</param>
/// <param name="radius">The radius of the glow.</param> /// <param name="radius">The radius of the glow.</param>
/// <param name="options">The options effecting blending and composition.</param> internal GlowProcessor(GraphicsOptions options, Color color, ValueSize radius)
internal GlowProcessor(Color color, ValueSize radius, GraphicsOptions options)
{ {
this.GlowColor = color; this.GlowColor = color;
this.Radius = radius; this.Radius = radius;
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary> /// <summary>
/// Gets the the radius. /// Gets the the radius.
/// </summary> /// </summary>
internal ValueSize Radius { get; } internal ValueSize Radius { get; }
/// <inheritdoc /> /// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle) public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)

10
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs

@ -17,16 +17,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// </summary> /// </summary>
/// <param name="color">The color of the vignette.</param> /// <param name="color">The color of the vignette.</param>
public VignetteProcessor(Color color) public VignetteProcessor(Color color)
: this(color, GraphicsOptions.Default) : this(new GraphicsOptions(), color)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="VignetteProcessor" /> class. /// Initializes a new instance of the <see cref="VignetteProcessor" /> class.
/// </summary> /// </summary>
/// <param name="color">The color of the vignette.</param>
/// <param name="options">The options effecting blending and composition.</param> /// <param name="options">The options effecting blending and composition.</param>
public VignetteProcessor(Color color, GraphicsOptions options) /// <param name="color">The color of the vignette.</param>
public VignetteProcessor(GraphicsOptions options, Color color)
{ {
this.VignetteColor = color; this.VignetteColor = color;
this.GraphicsOptions = options; this.GraphicsOptions = options;
@ -35,11 +35,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="VignetteProcessor" /> class. /// Initializes a new instance of the <see cref="VignetteProcessor" /> class.
/// </summary> /// </summary>
/// <param name="options">The options effecting blending and composition.</param>
/// <param name="color">The color of the vignette.</param> /// <param name="color">The color of the vignette.</param>
/// <param name="radiusX">The x-radius.</param> /// <param name="radiusX">The x-radius.</param>
/// <param name="radiusY">The y-radius.</param> /// <param name="radiusY">The y-radius.</param>
/// <param name="options">The options effecting blending and composition.</param> internal VignetteProcessor(GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY)
internal VignetteProcessor(Color color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options)
{ {
this.VignetteColor = color; this.VignetteColor = color;
this.RadiusX = radiusX; this.RadiusX = radiusX;

10
tests/ImageSharp.Benchmarks/Drawing/DrawText.cs

@ -1,4 +1,4 @@
// 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.Drawing; using System.Drawing;
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Benchmarks
graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var font = new Font("Arial", 12, GraphicsUnit.Point)) using (var font = new Font("Arial", 12, GraphicsUnit.Point))
{ {
graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); graphics.DrawString(this.TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780));
} }
} }
} }
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (var image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, this.TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))));
} }
} }
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (var image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, this.TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)));
} }
IImageProcessingContext DrawTextOldVersion( IImageProcessingContext DrawTextOldVersion(
@ -93,4 +93,4 @@ namespace SixLabors.ImageSharp.Benchmarks
} }
} }
} }
} }

14
tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs

@ -1,4 +1,4 @@
// 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.Drawing; using System.Drawing;
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Params(10, 100)] [Params(10, 100)]
public int TextIterations { get; set; } public int TextIterations { get; set; }
public string TextPhrase { get; set; } = "Hello World"; public string TextPhrase { get; set; } = "Hello World";
public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations)); public string TextToRender => string.Join(" ", Enumerable.Repeat(this.TextPhrase, this.TextIterations));
[Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")] [Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")]
public void DrawTextSystemDrawing() public void DrawTextSystemDrawing()
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (var font = new Font("Arial", 12, GraphicsUnit.Point)) using (var font = new Font("Arial", 12, GraphicsUnit.Point))
using (var gp = new GraphicsPath()) using (var gp = new GraphicsPath())
{ {
gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); gp.AddString(this.TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat());
graphics.DrawPath(pen, gp); graphics.DrawPath(pen, gp);
} }
} }
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (var image = new Image<Rgba32>(800, 800)) using (var image = new Image<Rgba32>(800, 800))
{ {
var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, this.TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))));
} }
} }
@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.Benchmarks
image.Mutate( image.Mutate(
x => DrawTextOldVersion( x => DrawTextOldVersion(
x, x,
new TextGraphicsOptions(true) { WrapTextWidth = 780 }, new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 },
TextToRender, this.TextToRender,
font, font,
null, null,
Processing.Pens.Solid(Rgba32.HotPink, 10), Processing.Pens.Solid(Rgba32.HotPink, 10),
@ -99,4 +99,4 @@ namespace SixLabors.ImageSharp.Benchmarks
} }
} }
} }
} }

2
tests/ImageSharp.Tests/Drawing/DrawImageTests.cs

@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
void Test() void Test()
{ {
background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions()));
} }
} }
} }

22
tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs

@ -1,4 +1,4 @@
// 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;
@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = new Pen(color, thickness); Pen pen = new Pen(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
} }
[Theory] [Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)]
public void DrawLines_Dash<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias) public void DrawLines_Dash<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias)
@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.Dash(color, thickness); Pen pen = Pens.Dash(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
} }
[Theory] [Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "LightGreen", 1f, 5, false)] [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "LightGreen", 1f, 5, false)]
public void DrawLines_Dot<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias) public void DrawLines_Dot<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias)
@ -46,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.Dot(color, thickness); Pen pen = Pens.Dot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
} }
[Theory] [Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Yellow", 1f, 5, false)] [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Yellow", 1f, 5, false)]
public void DrawLines_DashDot<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias) public void DrawLines_DashDot<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias)
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.DashDot(color, thickness); Pen pen = Pens.DashDot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
} }
@ -68,11 +68,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.DashDotDot(color, thickness); Pen pen = Pens.DashDotDot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
} }
private static void DrawLinesImpl<TPixel>( private static void DrawLinesImpl<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
string colorName, string colorName,
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) };
GraphicsOptions options = new GraphicsOptions(antialias); GraphicsOptions options = new GraphicsOptions { Antialias = antialias };
string aa = antialias ? "" : "_NoAntialias"; string aa = antialias ? "" : "_NoAntialias";
FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}";

2
tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}; };
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
GraphicsOptions options = new GraphicsOptions(antialias); GraphicsOptions options = new GraphicsOptions { Antialias = antialias };
string aa = antialias ? "" : "_NoAntialias"; string aa = antialias ? "" : "_NoAntialias";
FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}";

45
tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs

@ -1,4 +1,4 @@
// 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;
@ -16,6 +16,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using SixLabors.Shapes;
[GroupOutput("Drawing/GradientBrushes")] [GroupOutput("Drawing/GradientBrushes")]
public class FillLinearGradientBrushTests public class FillLinearGradientBrushTests
@ -392,5 +394,44 @@ namespace SixLabors.ImageSharp.Tests.Drawing
false, false,
false); false);
} }
[Theory]
[WithBlankImages(200, 200, PixelTypes.Rgba32)]
public void GradientsWithTransparencyOnExistingBackground<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
provider.VerifyOperation(
image =>
{
image.Mutate(i => i.Fill(Color.Red));
image.Mutate(ApplyGloss);
});
void ApplyGloss(IImageProcessingContext ctx)
{
Size size = ctx.GetCurrentSize();
IPathCollection glossPath = BuildGloss(size.Width, size.Height);
var graphicsOptions = new GraphicsOptions
{
Antialias = true,
ColorBlendingMode = PixelColorBlendingMode.Normal,
AlphaCompositionMode = PixelAlphaCompositionMode.SrcAtop
};
var linearGradientBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, size.Height / 2), GradientRepetitionMode.Repeat, new ColorStop(0, Color.White.WithAlpha(0.5f)), new ColorStop(1, Color.White.WithAlpha(0.25f)));
ctx.Fill(graphicsOptions, linearGradientBrush, glossPath);
}
IPathCollection BuildGloss(int imageWidth, int imageHeight)
{
var pathBuilder = new PathBuilder();
pathBuilder.AddLine(new PointF(0, 0), new PointF(imageWidth, 0));
pathBuilder.AddLine(new PointF(imageWidth, 0), new PointF(imageWidth, imageHeight * 0.4f));
pathBuilder.AddBezier(new PointF(imageWidth, imageHeight * 0.4f), new PointF(imageWidth / 2, imageHeight * 0.6f), new PointF(0, imageHeight * 0.4f));
pathBuilder.CloseFigure();
return new PathCollection(pathBuilder.Build());
}
}
} }
} }

2
tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}; };
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
var options = new GraphicsOptions(antialias); var options = new GraphicsOptions { Antialias = antialias };
string aa = antialias ? "" : "_NoAntialias"; string aa = antialias ? "" : "_NoAntialias";
FormattableString outputDetails = $"{colorName}_A{alpha}{aa}"; FormattableString outputDetails = $"{colorName}_A{alpha}{aa}";

22
tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs

@ -1,4 +1,4 @@
// 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.Numerics; using System.Numerics;
@ -16,8 +16,6 @@ using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Tests.Drawing namespace SixLabors.ImageSharp.Tests.Drawing
{ {
public class FillRegionProcessorTests public class FillRegionProcessorTests
{ {
@ -35,11 +33,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var brush = new Mock<IBrush>(); var brush = new Mock<IBrush>();
var region = new MockRegion2(bounds); var region = new MockRegion2(bounds);
var options = new GraphicsOptions(antialias) var options = new GraphicsOptions
{ {
Antialias = antialias,
AntialiasSubpixelDepth = 1 AntialiasSubpixelDepth = 1
}; };
var processor = new FillRegionProcessor(brush.Object, region, options); var processor = new FillRegionProcessor(options, brush.Object, region);
var img = new Image<Rgba32>(1, 1); var img = new Image<Rgba32>(1, 1);
processor.Execute(img, bounds); processor.Execute(img, bounds);
@ -51,8 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
var bounds = new Rectangle(-100, -10, 10, 10); var bounds = new Rectangle(-100, -10, 10, 10);
var brush = new Mock<IBrush>(); var brush = new Mock<IBrush>();
var options = new GraphicsOptions(true); var options = new GraphicsOptions { Antialias = true };
var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); var processor = new FillRegionProcessor(options, brush.Object, new MockRegion1());
var img = new Image<Rgba32>(10, 10); var img = new Image<Rgba32>(10, 10);
processor.Execute(img, bounds); processor.Execute(img, bounds);
} }
@ -73,11 +72,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
public void DoesNotThrowForIssue928() public void DoesNotThrowForIssue928()
{ {
var rectText = new RectangleF(0, 0, 2000, 2000); var rectText = new RectangleF(0, 0, 2000, 2000);
using (Image<Rgba32> img = new Image<Rgba32>((int)rectText.Width, (int)rectText.Height)) using (var img = new Image<Rgba32>((int)rectText.Width, (int)rectText.Height))
{ {
img.Mutate(x => x.Fill(Rgba32.Transparent)); img.Mutate(x => x.Fill(Rgba32.Transparent));
img.Mutate(ctx => { img.Mutate(ctx =>
{
ctx.DrawLines( ctx.DrawLines(
Rgba32.Red, Rgba32.Red,
0.984252f, 0.984252f,
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new PointF(104.782608f, 1075.13245f), new PointF(104.782608f, 1075.13245f),
new PointF(104.782608f, 1075.13245f) new PointF(104.782608f, 1075.13245f)
); );
} }
); );
} }
} }
@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Fact] [Fact]
public void DoesNotThrowFillingTriangle() public void DoesNotThrowFillingTriangle()
{ {
using(var image = new Image<Rgba32>(28, 28)) using (var image = new Image<Rgba32>(28, 28))
{ {
var path = new Polygon( var path = new Polygon(
new LinearLineSegment(new PointF(17.11f, 13.99659f), new PointF(14.01433f, 27.06201f)), new LinearLineSegment(new PointF(17.11f, 13.99659f), new PointF(14.01433f, 27.06201f)),

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

@ -156,10 +156,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{ {
TPixel bgColor = image[0, 0]; TPixel bgColor = image[0, 0];
var options = new GraphicsOptions(false) var options = new GraphicsOptions
{ {
ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage Antialias = false,
}; ColorBlendingMode = blenderMode,
BlendPercentage = blendPercentage
};
if (triggerFillRegion) if (triggerFillRegion)
{ {
@ -173,13 +175,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
} }
var testOutputDetails = new var testOutputDetails = new
{ {
triggerFillRegion = triggerFillRegion, triggerFillRegion = triggerFillRegion,
newColorName = newColorName, newColorName = newColorName,
alpha = alpha, alpha = alpha,
blenderMode = blenderMode, blenderMode = blenderMode,
blendPercentage = blendPercentage blendPercentage = blendPercentage
}; };
image.DebugSave( image.DebugSave(
provider, provider,

17
tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs

@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing; using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes; using SixLabors.Shapes;
using Xunit; using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
public class DrawPathCollection : BaseImageOperationsExtensionTest public class DrawPathCollection : BaseImageOperationsExtensionTest
{ {
GraphicsOptions noneDefault = new GraphicsOptions(); private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false };
Color color = Color.HotPink; Color color = Color.HotPink;
Pen pen = Pens.Solid(Rgba32.HotPink, 1); Pen pen = Pens.Solid(Rgba32.HotPink, 1);
IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapePath region = Assert.IsType<ShapePath>(processor.Region); ShapePath region = Assert.IsType<ShapePath>(processor.Region);
@ -60,13 +63,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsBrushPathOptions() public void CorrectlySetsBrushPathOptions()
{ {
this.operations.Draw(this.noneDefault, this.pen, this.pathCollection); this.operations.Draw(this.nonDefault, this.pen, this.pathCollection);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapePath region = Assert.IsType<ShapePath>(processor.Region); ShapePath region = Assert.IsType<ShapePath>(processor.Region);
Assert.IsType<ComplexPolygon>(region.Shape); Assert.IsType<ComplexPolygon>(region.Shape);
@ -84,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapePath region = Assert.IsType<ShapePath>(processor.Region); ShapePath region = Assert.IsType<ShapePath>(processor.Region);
Assert.IsType<ComplexPolygon>(region.Shape); Assert.IsType<ComplexPolygon>(region.Shape);
@ -97,13 +100,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsColorPathAndOptions() public void CorrectlySetsColorPathAndOptions()
{ {
this.operations.Draw(this.noneDefault, this.color, 1, this.pathCollection); this.operations.Draw(this.nonDefault, this.color, 1, this.pathCollection);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapePath region = Assert.IsType<ShapePath>(processor.Region); ShapePath region = Assert.IsType<ShapePath>(processor.Region);
Assert.IsType<ComplexPolygon>(region.Shape); Assert.IsType<ComplexPolygon>(region.Shape);

17
tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs

@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing; using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes; using SixLabors.Shapes;
using Xunit; using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
public class FillPath : BaseImageOperationsExtensionTest public class FillPath : BaseImageOperationsExtensionTest
{ {
GraphicsOptions noneDefault = new GraphicsOptions(); private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false };
Color color = Color.HotPink; Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
IPath path = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { IPath path = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
@ -30,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
this.operations.Fill(this.brush, this.path); this.operations.Fill(this.brush, this.path);
var processor = this.Verify<FillRegionProcessor>(); var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
@ -44,10 +47,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsBrushPathOptions() public void CorrectlySetsBrushPathOptions()
{ {
this.operations.Fill(this.noneDefault, this.brush, this.path); this.operations.Fill(this.nonDefault, this.brush, this.path);
var processor = this.Verify<FillRegionProcessor>(); var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -62,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
this.operations.Fill(this.color, this.path); this.operations.Fill(this.color, this.path);
var processor = this.Verify<FillRegionProcessor>(); var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -75,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsColorPathAndOptions() public void CorrectlySetsColorPathAndOptions()
{ {
this.operations.Fill(this.noneDefault, this.color, this.path); this.operations.Fill(this.nonDefault, this.color, this.path);
var processor = this.Verify<FillRegionProcessor>(); var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);

17
tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs

@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing; using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes; using SixLabors.Shapes;
using Xunit; using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
public class FillPathCollection : BaseImageOperationsExtensionTest public class FillPathCollection : BaseImageOperationsExtensionTest
{ {
GraphicsOptions noneDefault = new GraphicsOptions(); private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false };
Color color = Color.HotPink; Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
@ -61,13 +64,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsBrushPathOptions() public void CorrectlySetsBrushPathOptions()
{ {
this.operations.Fill(this.noneDefault, this.brush, this.pathCollection); this.operations.Fill(this.nonDefault, this.brush, this.pathCollection);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -86,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -100,13 +103,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsColorPathAndOptions() public void CorrectlySetsColorPathAndOptions()
{ {
this.operations.Fill(this.noneDefault, this.color, this.pathCollection); this.operations.Fill(this.nonDefault, this.color, this.pathCollection);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i); FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);

17
tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs

@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing; using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes; using SixLabors.Shapes;
using Xunit; using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
public class FillPolygon : BaseImageOperationsExtensionTest public class FillPolygon : BaseImageOperationsExtensionTest
{ {
GraphicsOptions noneDefault = new GraphicsOptions(); private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false };
Color color = Color.HotPink; Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
SixLabors.Primitives.PointF[] path = { SixLabors.Primitives.PointF[] path = {
@ -32,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -44,10 +47,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsBrushPathAndOptions() public void CorrectlySetsBrushPathAndOptions()
{ {
this.operations.FillPolygon(this.noneDefault, this.brush, this.path); this.operations.FillPolygon(this.nonDefault, this.brush, this.path);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -63,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -76,10 +79,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsColorPathAndOptions() public void CorrectlySetsColorPathAndOptions()
{ {
this.operations.FillPolygon(this.noneDefault, this.color, this.path); this.operations.FillPolygon(this.nonDefault, this.color, this.path);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape); Polygon polygon = Assert.IsType<Polygon>(region.Shape);

26
tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs

@ -1,4 +1,4 @@
// 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 SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -6,17 +6,19 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing; using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing.Paths namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{ {
public class FillRectangle : BaseImageOperationsExtensionTest public class FillRectangle : BaseImageOperationsExtensionTest
{ {
GraphicsOptions noneDefault = new GraphicsOptions(); private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink); private GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false };
SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); private Color color = Color.HotPink;
private SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
private SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76);
[Fact] [Fact]
public void CorrectlySetsBrushAndRectangle() public void CorrectlySetsBrushAndRectangle()
@ -24,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
this.operations.Fill(this.brush, this.rectangle); this.operations.Fill(this.brush, this.rectangle);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape); Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape);
@ -39,10 +41,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsBrushRectangleAndOptions() public void CorrectlySetsBrushRectangleAndOptions()
{ {
this.operations.Fill(this.noneDefault, this.brush, this.rectangle); this.operations.Fill(this.nonDefault, this.brush, this.rectangle);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape); Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape);
@ -60,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
this.operations.Fill(this.color, this.rectangle); this.operations.Fill(this.color, this.rectangle);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options); Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape); Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape);
@ -76,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact] [Fact]
public void CorrectlySetsColorRectangleAndOptions() public void CorrectlySetsColorRectangleAndOptions()
{ {
this.operations.Fill(this.noneDefault, this.color, this.rectangle); this.operations.Fill(this.nonDefault, this.color, this.rectangle);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(); FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options); Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region); ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape); Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape);

29
tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs

@ -1,4 +1,4 @@
// 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.Collections.Generic; using System.Collections.Generic;
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
} }
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
@ -46,7 +46,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
Color.DarkBlue, Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)
) )
.Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, .Fill(
new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink, Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))
); );
@ -73,12 +74,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink, Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.Transparent, Color.Transparent,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
); );
@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink, Color.HotPink,
new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY)));
@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
transparentRed, transparentRed,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
); );
@ -130,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
PixelAlphaCompositionMode composition) PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using(Image<TPixel> dstImg = provider.GetImage(), srcImg = provider.GetImage()) using (Image<TPixel> dstImg = provider.GetImage(), srcImg = provider.GetImage())
{ {
int scaleX = (dstImg.Width / 100); int scaleX = (dstImg.Width / 100);
int scaleY = (dstImg.Height / 100); int scaleY = (dstImg.Height / 100);
@ -146,13 +147,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
dstImg.Mutate( dstImg.Mutate(
x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) x => x.DrawImage(srcImg, new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition })
); );
VerifyImage(provider, blending, composition, dstImg); VerifyImage(provider, blending, composition, dstImg);
} }
} }
private static void VerifyImage<TPixel>( private static void VerifyImage<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending, PixelColorBlendingMode blending,
@ -165,13 +166,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new { composition, blending }, new { composition, blending },
appendPixelTypeToFileName: false, appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false); appendSourceFileOrDescription: false);
var comparer = ImageComparer.TolerantPercentage(0.01f, 3); var comparer = ImageComparer.TolerantPercentage(0.01f, 3);
img.CompareFirstFrameToReferenceOutput(comparer, img.CompareFirstFrameToReferenceOutput(comparer,
provider, provider,
new { composition, blending }, new { composition, blending },
appendPixelTypeToFileName: false, appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false); appendSourceFileOrDescription: false);
} }
} }
} }

12
tests/ImageSharp.Tests/Drawing/Text/DrawText.cs

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
public void FillsForEachACharacterWhenBrushSetAndNotPen() public void FillsForEachACharacterWhenBrushSetAndNotPen()
{ {
this.operations.DrawText( this.operations.DrawText(
new TextGraphicsOptions(true), new TextGraphicsOptions { Antialias = true },
"123", "123",
this.Font, this.Font,
Brushes.Solid(Color.Red), Brushes.Solid(Color.Red),
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact] [Fact]
public void FillsForEachACharacterWhenBrushSet() public void FillsForEachACharacterWhenBrushSet()
{ {
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); this.operations.DrawText(new TextGraphicsOptions { Antialias = true }, "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero);
this.Verify<DrawTextProcessor>(0); this.Verify<DrawTextProcessor>(0);
} }
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact] [Fact]
public void FillsForEachACharacterWhenColorSet() public void FillsForEachACharacterWhenColorSet()
{ {
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Color.Red, Vector2.Zero); this.operations.DrawText(new TextGraphicsOptions { Antialias = true }, "123", this.Font, Color.Red, Vector2.Zero);
var processor = this.Verify<DrawTextProcessor>(0); var processor = this.Verify<DrawTextProcessor>(0);
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
public void DrawForEachACharacterWhenPenSetAndNotBrush() public void DrawForEachACharacterWhenPenSetAndNotBrush()
{ {
this.operations.DrawText( this.operations.DrawText(
new TextGraphicsOptions(true), new TextGraphicsOptions { Antialias = true },
"123", "123",
this.Font, this.Font,
null, null,
@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact] [Fact]
public void DrawForEachACharacterWhenPenSet() public void DrawForEachACharacterWhenPenSet()
{ {
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); this.operations.DrawText(new TextGraphicsOptions { Antialias = true }, "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero);
this.Verify<DrawTextProcessor>(0); this.Verify<DrawTextProcessor>(0);
} }
@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
public void DrawForEachACharacterWhenPenSetAndFillFroEachWhenBrushSet() public void DrawForEachACharacterWhenPenSetAndFillFroEachWhenBrushSet()
{ {
this.operations.DrawText( this.operations.DrawText(
new TextGraphicsOptions(true), new TextGraphicsOptions { Antialias = true },
"123", "123",
this.Font, this.Font,
Brushes.Solid(Color.Red), Brushes.Solid(Color.Red),

9
tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs

@ -55,8 +55,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
var scaledFont = new Font(font, scalingFactor * font.Size); var scaledFont = new Font(font, scalingFactor * font.Size);
var center = new PointF(img.Width / 2, img.Height / 2); var center = new PointF(img.Width / 2, img.Height / 2);
var textGraphicOptions = new TextGraphicsOptions(true) var textGraphicOptions = new TextGraphicsOptions
{ {
Antialias = true,
HorizontalAlignment = HorizontalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center VerticalAlignment = VerticalAlignment.Center
}; };
@ -222,7 +223,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
string text = Repeat("Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\n", string text = Repeat("Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\n",
20); 20);
var textOptions = new TextGraphicsOptions(true) { WrapTextWidth = 1000 }; var textOptions = new TextGraphicsOptions
{
Antialias = true,
WrapTextWidth = 1000
};
string details = fontName.Replace(" ", ""); string details = fontName.Replace(" ", "");

183
tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs

@ -1,6 +1,8 @@
// 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 SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using Xunit; using Xunit;
@ -9,16 +11,184 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{ {
public class TextGraphicsOptionsTests public class TextGraphicsOptionsTests
{ {
private readonly TextGraphicsOptions newTextGraphicsOptions = new TextGraphicsOptions();
private readonly TextGraphicsOptions cloneTextGraphicsOptions = new TextGraphicsOptions().DeepClone();
[Fact]
public void CloneTextGraphicsOptionsIsNotNull() => Assert.True(this.cloneTextGraphicsOptions != null);
[Fact]
public void DefaultTextGraphicsOptionsAntialias()
{
Assert.True(this.newTextGraphicsOptions.Antialias);
Assert.True(this.cloneTextGraphicsOptions.Antialias);
}
[Fact]
public void DefaultTextGraphicsOptionsAntialiasSuppixelDepth()
{
const int Expected = 16;
Assert.Equal(Expected, this.newTextGraphicsOptions.AntialiasSubpixelDepth);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.AntialiasSubpixelDepth);
}
[Fact]
public void DefaultTextGraphicsOptionsBlendPercentage()
{
const float Expected = 1F;
Assert.Equal(Expected, this.newTextGraphicsOptions.BlendPercentage);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.BlendPercentage);
}
[Fact]
public void DefaultTextGraphicsOptionsColorBlendingMode()
{
const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal;
Assert.Equal(Expected, this.newTextGraphicsOptions.ColorBlendingMode);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.ColorBlendingMode);
}
[Fact]
public void DefaultTextGraphicsOptionsAlphaCompositionMode()
{
const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver;
Assert.Equal(Expected, this.newTextGraphicsOptions.AlphaCompositionMode);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.AlphaCompositionMode);
}
[Fact]
public void DefaultTextGraphicsOptionsApplyKerning()
{
const bool Expected = true;
Assert.Equal(Expected, this.newTextGraphicsOptions.ApplyKerning);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.ApplyKerning);
}
[Fact]
public void DefaultTextGraphicsOptionsHorizontalAlignment()
{
const HorizontalAlignment Expected = HorizontalAlignment.Left;
Assert.Equal(Expected, this.newTextGraphicsOptions.HorizontalAlignment);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.HorizontalAlignment);
}
[Fact]
public void DefaultTextGraphicsOptionsVerticalAlignment()
{
const VerticalAlignment Expected = VerticalAlignment.Top;
Assert.Equal(Expected, this.newTextGraphicsOptions.VerticalAlignment);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.VerticalAlignment);
}
[Fact]
public void DefaultTextGraphicsOptionsDpiX()
{
const float Expected = 72F;
Assert.Equal(Expected, this.newTextGraphicsOptions.DpiX);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.DpiX);
}
[Fact]
public void DefaultTextGraphicsOptionsDpiY()
{
const float Expected = 72F;
Assert.Equal(Expected, this.newTextGraphicsOptions.DpiY);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.DpiY);
}
[Fact]
public void DefaultTextGraphicsOptionsTabWidth()
{
const float Expected = 4F;
Assert.Equal(Expected, this.newTextGraphicsOptions.TabWidth);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.TabWidth);
}
[Fact]
public void DefaultTextGraphicsOptionsWrapTextWidth()
{
const float Expected = 0F;
Assert.Equal(Expected, this.newTextGraphicsOptions.WrapTextWidth);
Assert.Equal(Expected, this.cloneTextGraphicsOptions.WrapTextWidth);
}
[Fact]
public void NonDefaultClone()
{
var expected = new TextGraphicsOptions
{
AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop,
Antialias = false,
AntialiasSubpixelDepth = 23,
ApplyKerning = false,
BlendPercentage = .25F,
ColorBlendingMode = PixelColorBlendingMode.HardLight,
DpiX = 46F,
DpiY = 52F,
HorizontalAlignment = HorizontalAlignment.Center,
TabWidth = 3F,
VerticalAlignment = VerticalAlignment.Bottom,
WrapTextWidth = 42F
};
TextGraphicsOptions actual = expected.DeepClone();
Assert.Equal(expected.AlphaCompositionMode, actual.AlphaCompositionMode);
Assert.Equal(expected.Antialias, actual.Antialias);
Assert.Equal(expected.AntialiasSubpixelDepth, actual.AntialiasSubpixelDepth);
Assert.Equal(expected.ApplyKerning, actual.ApplyKerning);
Assert.Equal(expected.BlendPercentage, actual.BlendPercentage);
Assert.Equal(expected.ColorBlendingMode, actual.ColorBlendingMode);
Assert.Equal(expected.DpiX, actual.DpiX);
Assert.Equal(expected.DpiY, actual.DpiY);
Assert.Equal(expected.HorizontalAlignment, actual.HorizontalAlignment);
Assert.Equal(expected.TabWidth, actual.TabWidth);
Assert.Equal(expected.VerticalAlignment, actual.VerticalAlignment);
Assert.Equal(expected.WrapTextWidth, actual.WrapTextWidth);
}
[Fact]
public void CloneIsDeep()
{
var expected = new TextGraphicsOptions();
TextGraphicsOptions actual = expected.DeepClone();
actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop;
actual.Antialias = false;
actual.AntialiasSubpixelDepth = 23;
actual.ApplyKerning = false;
actual.BlendPercentage = .25F;
actual.ColorBlendingMode = PixelColorBlendingMode.HardLight;
actual.DpiX = 46F;
actual.DpiY = 52F;
actual.HorizontalAlignment = HorizontalAlignment.Center;
actual.TabWidth = 3F;
actual.VerticalAlignment = VerticalAlignment.Bottom;
actual.WrapTextWidth = 42F;
Assert.NotEqual(expected.AlphaCompositionMode, actual.AlphaCompositionMode);
Assert.NotEqual(expected.Antialias, actual.Antialias);
Assert.NotEqual(expected.AntialiasSubpixelDepth, actual.AntialiasSubpixelDepth);
Assert.NotEqual(expected.ApplyKerning, actual.ApplyKerning);
Assert.NotEqual(expected.BlendPercentage, actual.BlendPercentage);
Assert.NotEqual(expected.ColorBlendingMode, actual.ColorBlendingMode);
Assert.NotEqual(expected.DpiX, actual.DpiX);
Assert.NotEqual(expected.DpiY, actual.DpiY);
Assert.NotEqual(expected.HorizontalAlignment, actual.HorizontalAlignment);
Assert.NotEqual(expected.TabWidth, actual.TabWidth);
Assert.NotEqual(expected.VerticalAlignment, actual.VerticalAlignment);
Assert.NotEqual(expected.WrapTextWidth, actual.WrapTextWidth);
}
[Fact] [Fact]
public void ExplicitCastOfGraphicsOptions() public void ExplicitCastOfGraphicsOptions()
{ {
var opt = new GraphicsOptions(false) TextGraphicsOptions textOptions = new GraphicsOptions
{ {
Antialias = false,
AntialiasSubpixelDepth = 99 AntialiasSubpixelDepth = 99
}; };
TextGraphicsOptions textOptions = opt;
Assert.False(textOptions.Antialias); Assert.False(textOptions.Antialias);
Assert.Equal(99, textOptions.AntialiasSubpixelDepth); Assert.Equal(99, textOptions.AntialiasSubpixelDepth);
} }
@ -26,8 +196,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact] [Fact]
public void ImplicitCastToGraphicsOptions() public void ImplicitCastToGraphicsOptions()
{ {
var textOptions = new TextGraphicsOptions(false) var textOptions = new TextGraphicsOptions
{ {
Antialias = false,
AntialiasSubpixelDepth = 99 AntialiasSubpixelDepth = 99
}; };
@ -37,4 +208,4 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Assert.Equal(99, opt.AntialiasSubpixelDepth); Assert.Equal(99, opt.AntialiasSubpixelDepth);
} }
} }
} }

93
tests/ImageSharp.Tests/GraphicsOptionsTests.cs

@ -1,21 +1,100 @@
// 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 SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests
{ {
public class GraphicsOptionsTests public class GraphicsOptionsTests
{ {
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
private readonly GraphicsOptions newGraphicsOptions = new GraphicsOptions();
private readonly GraphicsOptions cloneGraphicsOptions = new GraphicsOptions().DeepClone();
[Fact]
public void CloneGraphicsOptionsIsNotNull() => Assert.True(this.cloneGraphicsOptions != null);
[Fact]
public void DefaultGraphicsOptionsAntialias()
{
Assert.True(this.newGraphicsOptions.Antialias);
Assert.True(this.cloneGraphicsOptions.Antialias);
}
[Fact]
public void DefaultGraphicsOptionsAntialiasSuppixelDepth()
{
const int Expected = 16;
Assert.Equal(Expected, this.newGraphicsOptions.AntialiasSubpixelDepth);
Assert.Equal(Expected, this.cloneGraphicsOptions.AntialiasSubpixelDepth);
}
[Fact]
public void DefaultGraphicsOptionsBlendPercentage()
{
const float Expected = 1F;
Assert.Equal(Expected, this.newGraphicsOptions.BlendPercentage);
Assert.Equal(Expected, this.cloneGraphicsOptions.BlendPercentage);
}
[Fact]
public void DefaultGraphicsOptionsColorBlendingMode()
{
const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal;
Assert.Equal(Expected, this.newGraphicsOptions.ColorBlendingMode);
Assert.Equal(Expected, this.cloneGraphicsOptions.ColorBlendingMode);
}
[Fact]
public void DefaultGraphicsOptionsAlphaCompositionMode()
{
const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver;
Assert.Equal(Expected, this.newGraphicsOptions.AlphaCompositionMode);
Assert.Equal(Expected, this.cloneGraphicsOptions.AlphaCompositionMode);
}
[Fact]
public void NonDefaultClone()
{
var expected = new GraphicsOptions
{
AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop,
Antialias = false,
AntialiasSubpixelDepth = 23,
BlendPercentage = .25F,
ColorBlendingMode = PixelColorBlendingMode.HardLight,
};
GraphicsOptions actual = expected.DeepClone();
Assert.Equal(expected, actual, graphicsOptionsComparer);
}
[Fact]
public void CloneIsDeep()
{
var expected = new GraphicsOptions();
GraphicsOptions actual = expected.DeepClone();
actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop;
actual.Antialias = false;
actual.AntialiasSubpixelDepth = 23;
actual.BlendPercentage = .25F;
actual.ColorBlendingMode = PixelColorBlendingMode.HardLight;
Assert.NotEqual(expected, actual, graphicsOptionsComparer);
}
[Fact] [Fact]
public void IsOpaqueColor() public void IsOpaqueColor()
{ {
Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Red)); Assert.True(new GraphicsOptions().IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(Rgba32.Red)); Assert.False(new GraphicsOptions { BlendPercentage = .5F }.IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Transparent)); Assert.False(new GraphicsOptions().IsOpaqueColorWithoutBlending(Rgba32.Transparent));
Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); Assert.False(new GraphicsOptions { ColorBlendingMode = PixelColorBlendingMode.Lighten, BlendPercentage = 1F }.IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); Assert.False(new GraphicsOptions { ColorBlendingMode = PixelColorBlendingMode.Normal, AlphaCompositionMode = PixelAlphaCompositionMode.DestOver, BlendPercentage = 1f }.IsOpaqueColorWithoutBlending(Rgba32.Red));
} }
} }
} }

4
tests/ImageSharp.Tests/Issues/Issue412.cs

@ -20,14 +20,14 @@ namespace SixLabors.ImageSharp.Tests.Issues
for (var i = 0; i < 40; ++i) for (var i = 0; i < 40; ++i)
{ {
context.DrawLines( context.DrawLines(
new GraphicsOptions(false), new GraphicsOptions { Antialias = false },
Color.Black, Color.Black,
1, 1,
new PointF(i, 0.1066f), new PointF(i, 0.1066f),
new PointF(i, 10.1066f)); new PointF(i, 10.1066f));
context.DrawLines( context.DrawLines(
new GraphicsOptions(false), new GraphicsOptions { Antialias = false },
Color.Red, Color.Red,
1, 1,
new PointF(i, 15.1066f), new PointF(i, 15.1066f),

11
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs

@ -1,4 +1,4 @@
// 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.
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
@ -36,9 +36,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
using (Image<Rgba32> src = srcFile.CreateRgba32Image()) using (Image<Rgba32> src = srcFile.CreateRgba32Image())
using (Image<Rgba32> dest = provider.GetImage()) using (Image<Rgba32> dest = provider.GetImage())
{ {
GraphicsOptions options = new GraphicsOptions var options = new GraphicsOptions
{ {
AlphaCompositionMode = mode Antialias = false,
AlphaCompositionMode = mode
}; };
using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options))) using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options)))
@ -53,4 +54,4 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
} }
} }
} }
} }

2
tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Processing
public BaseImageOperationsExtensionTest() public BaseImageOperationsExtensionTest()
{ {
this.options = new GraphicsOptions(false); this.options = new GraphicsOptions { Antialias = false };
this.source = new Image<Rgba32>(91 + 324, 123 + 56); this.source = new Image<Rgba32>(91 + 324, 123 + 56);
this.rect = new Rectangle(91, 123, 324, 56); // make this random? this.rect = new Rectangle(91, 123, 324, 56); // make this random?
this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations<Rgba32>(this.source, false); this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations<Rgba32>(this.source, false);

24
tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs

@ -1,22 +1,24 @@
// 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 SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Effects namespace SixLabors.ImageSharp.Tests.Processing.Effects
{ {
public class BackgroundColorTest : BaseImageOperationsExtensionTest public class BackgroundColorTest : BaseImageOperationsExtensionTest
{ {
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
[Fact] [Fact]
public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet() public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet()
{ {
this.operations.BackgroundColor(Color.BlanchedAlmond); this.operations.BackgroundColor(Color.BlanchedAlmond);
var processor = this.Verify<BackgroundColorProcessor>(); BackgroundColorProcessor processor = this.Verify<BackgroundColorProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); Assert.Equal(new GraphicsOptions(), processor.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.BlanchedAlmond, processor.Color); Assert.Equal(Color.BlanchedAlmond, processor.Color);
} }
@ -24,9 +26,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet() public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet()
{ {
this.operations.BackgroundColor(Color.BlanchedAlmond, this.rect); this.operations.BackgroundColor(Color.BlanchedAlmond, this.rect);
var processor = this.Verify<BackgroundColorProcessor>(this.rect); BackgroundColorProcessor processor = this.Verify<BackgroundColorProcessor>(this.rect);
Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); Assert.Equal(new GraphicsOptions(), processor.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.BlanchedAlmond, processor.Color); Assert.Equal(Color.BlanchedAlmond, processor.Color);
} }
@ -34,9 +36,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet() public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet()
{ {
this.operations.BackgroundColor(this.options, Color.BlanchedAlmond); this.operations.BackgroundColor(this.options, Color.BlanchedAlmond);
var processor = this.Verify<BackgroundColorProcessor>(); BackgroundColorProcessor processor = this.Verify<BackgroundColorProcessor>();
Assert.Equal(this.options, processor.GraphicsOptions); Assert.Equal(this.options, processor.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.BlanchedAlmond, processor.Color); Assert.Equal(Color.BlanchedAlmond, processor.Color);
} }
@ -44,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet() public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet()
{ {
this.operations.BackgroundColor(this.options, Color.BlanchedAlmond, this.rect); this.operations.BackgroundColor(this.options, Color.BlanchedAlmond, this.rect);
var processor = this.Verify<BackgroundColorProcessor>(this.rect); BackgroundColorProcessor processor = this.Verify<BackgroundColorProcessor>(this.rect);
Assert.Equal(this.options, processor.GraphicsOptions); Assert.Equal(this.options, processor.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.BlanchedAlmond, processor.Color); Assert.Equal(Color.BlanchedAlmond, processor.Color);
} }
} }
} }

23
tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs

@ -1,10 +1,11 @@
// 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 SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit; using Xunit;
@ -12,13 +13,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{ {
public class GlowTest : BaseImageOperationsExtensionTest public class GlowTest : BaseImageOperationsExtensionTest
{ {
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
[Fact] [Fact]
public void Glow_GlowProcessorWithDefaultValues() public void Glow_GlowProcessorWithDefaultValues()
{ {
this.operations.Glow(); this.operations.Glow();
var p = this.Verify<GlowProcessor>(); GlowProcessor p = this.Verify<GlowProcessor>();
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(Color.Black, p.GlowColor);
Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius);
} }
@ -27,9 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Glow_Color_GlowProcessorWithDefaultValues() public void Glow_Color_GlowProcessorWithDefaultValues()
{ {
this.operations.Glow(Rgba32.Aquamarine); this.operations.Glow(Rgba32.Aquamarine);
var p = this.Verify<GlowProcessor>(); GlowProcessor p = this.Verify<GlowProcessor>();
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Aquamarine, p.GlowColor); Assert.Equal(Color.Aquamarine, p.GlowColor);
Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius);
} }
@ -38,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Glow_Radux_GlowProcessorWithDefaultValues() public void Glow_Radux_GlowProcessorWithDefaultValues()
{ {
this.operations.Glow(3.5f); this.operations.Glow(3.5f);
var p = this.Verify<GlowProcessor>(); GlowProcessor p = this.Verify<GlowProcessor>();
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(Color.Black, p.GlowColor);
Assert.Equal(ValueSize.Absolute(3.5f), p.Radius); Assert.Equal(ValueSize.Absolute(3.5f), p.Radius);
} }
@ -50,11 +53,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{ {
var rect = new Rectangle(12, 123, 43, 65); var rect = new Rectangle(12, 123, 43, 65);
this.operations.Glow(rect); this.operations.Glow(rect);
var p = this.Verify<GlowProcessor>(rect); GlowProcessor p = this.Verify<GlowProcessor>(rect);
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(Color.Black, p.GlowColor);
Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius);
} }
} }
} }

23
tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs

@ -1,9 +1,10 @@
// 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 SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit; using Xunit;
@ -11,13 +12,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{ {
public class VignetteTest : BaseImageOperationsExtensionTest public class VignetteTest : BaseImageOperationsExtensionTest
{ {
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
[Fact] [Fact]
public void Vignette_VignetteProcessorWithDefaultValues() public void Vignette_VignetteProcessorWithDefaultValues()
{ {
this.operations.Vignette(); this.operations.Vignette();
var p = this.Verify<VignetteProcessor>(); VignetteProcessor p = this.Verify<VignetteProcessor>();
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(Color.Black, p.VignetteColor);
Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX);
Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY);
@ -27,9 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Vignette_Color_VignetteProcessorWithDefaultValues() public void Vignette_Color_VignetteProcessorWithDefaultValues()
{ {
this.operations.Vignette(Color.Aquamarine); this.operations.Vignette(Color.Aquamarine);
var p = this.Verify<VignetteProcessor>(); VignetteProcessor p = this.Verify<VignetteProcessor>();
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Aquamarine, p.VignetteColor); Assert.Equal(Color.Aquamarine, p.VignetteColor);
Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX);
Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY);
@ -39,9 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Vignette_Radux_VignetteProcessorWithDefaultValues() public void Vignette_Radux_VignetteProcessorWithDefaultValues()
{ {
this.operations.Vignette(3.5f, 12123f); this.operations.Vignette(3.5f, 12123f);
var p = this.Verify<VignetteProcessor>(); VignetteProcessor p = this.Verify<VignetteProcessor>();
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(Color.Black, p.VignetteColor);
Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX); Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX);
Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY); Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY);
@ -52,12 +55,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{ {
var rect = new Rectangle(12, 123, 43, 65); var rect = new Rectangle(12, 123, 43, 65);
this.operations.Vignette(rect); this.operations.Vignette(rect);
var p = this.Verify<VignetteProcessor>(rect); VignetteProcessor p = this.Verify<VignetteProcessor>(rect);
Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer);
Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(Color.Black, p.VignetteColor);
Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX);
Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY);
} }
} }
} }

21
tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Tests.TestUtilities
{
public class GraphicsOptionsComparer : IEqualityComparer<GraphicsOptions>
{
public bool Equals(GraphicsOptions x, GraphicsOptions y)
{
return x.AlphaCompositionMode == y.AlphaCompositionMode
&& x.Antialias == y.Antialias
&& x.AntialiasSubpixelDepth == y.AntialiasSubpixelDepth
&& x.BlendPercentage == y.BlendPercentage
&& x.ColorBlendingMode == y.ColorBlendingMode;
}
public int GetHashCode(GraphicsOptions obj) => obj.GetHashCode();
}
}

2
tests/Images/External

@ -1 +1 @@
Subproject commit 563ec6f7774734ba39924174c8961705a1ea6fa2 Subproject commit f0c4033667bd23ad9dde82ccb625c232d402ee05
Loading…
Cancel
Save