Browse Source

Merge remote-tracking branch 'upstream/master' into feature/tga

af/merge-core
Brian Popow 6 years ago
parent
commit
6a6e7ca050
  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

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.
using System;
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Primitives
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);
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.
using System;
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing
{
/// <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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <seealso cref="System.IDisposable" />
public abstract class BrushApplicator<TPixel> : IDisposable // disposable will be required if/when there is an ImageBrush
/// <seealso cref="IDisposable" />
public abstract class BrushApplicator<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="BrushApplicator{TPixel}"/> class.
/// </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="options">The options.</param>
internal BrushApplicator(ImageFrame<TPixel> target, GraphicsOptions options)
internal BrushApplicator(Configuration configuration, GraphicsOptions options, ImageFrame<TPixel> target)
{
this.Configuration = configuration;
this.Target = target;
this.Options = options;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(options);
}
/// <summary>
/// Gets the blender
/// Gets the configuration instance to use when performing operations.
/// </summary>
protected Configuration Configuration { get; }
/// <summary>
/// Gets the pixel blender.
/// </summary>
internal PixelBlender<TPixel> Blender { get; }
/// <summary>
/// Gets the destination
/// Gets the target image.
/// </summary>
protected ImageFrame<TPixel> Target { get; }
/// <summary>
/// Gets the blend percentage
/// Gets thegraphics options
/// </summary>
protected GraphicsOptions Options { get; }
/// <summary>
/// Gets the color for a single pixel.
/// Gets the overlay pixel at the specified position.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <returns>The a <typeparamref name="TPixel"/> that should be applied to the pixel.</returns>
/// <param name="x">The x-coordinate.</param>
/// <param name="y">The y-coordinate.</param>
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
internal abstract TPixel this[int x, int y] { get; }
/// <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>
/// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
/// </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="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="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="y">The y-position in the target pixel space that whole scanline corresponds to.</param>
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(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<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.Memory.Span;
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);
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.
using System;
@ -47,12 +47,14 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
RectangleF region) =>
new RadialGradientBrushApplicator<TPixel>(
source,
configuration,
options,
source,
this.center,
this.referenceAxisEnd,
this.axisRatio,
@ -86,24 +88,26 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options</param>
/// <param name="center">Center of the ellipse</param>
/// <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="center">Center of the ellipse.</param>
/// <param name="referenceAxisEnd">Point on one angular points of the ellipse.</param>
/// <param name="axisRatio">
/// 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>
/// <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>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> target,
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> target,
PointF center,
PointF referenceAxisEnd,
float axisRatio,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
: base(configuration, options, target, colorStops, repetitionMode)
{
this.center = center;
this.referenceAxisEnd = referenceAxisEnd;
@ -122,11 +126,6 @@ namespace SixLabors.ImageSharp.Processing
this.cosRotation = (float)Math.Cos(this.rotation);
}
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc />
protected override float PositionOnGradient(float xt, float yt)
{
@ -139,16 +138,13 @@ namespace SixLabors.ImageSharp.Processing
float xSquared = x * x;
float ySquared = y * y;
var inBoundaryChecker = (xSquared / this.referenceRadiusSquared)
+ (ySquared / this.secondRadiusSquared);
return inBoundaryChecker;
return (xSquared / this.referenceRadiusSquared) + (ySquared / this.secondRadiusSquared);
}
private float AngleBetween(PointF junction, PointF a, PointF b)
{
var vA = a - junction;
var vB = b - junction;
PointF vA = a - junction;
PointF vB = b - junction;
return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X);
}
@ -156,6 +152,7 @@ namespace SixLabors.ImageSharp.Processing
PointF p1,
PointF p2)
{
// TODO: Can we not just use Vector2 distance here?
float dX = p1.X - p2.X;
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(
this IImageProcessingContext source,
Image image,
float opacity) =>
source.ApplyProcessor(
float opacity)
{
var options = new GraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
GraphicsOptions.Default.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
image,
Point.Empty,
options.ColorBlendingMode,
options.AlphaCompositionMode,
opacity));
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
@ -49,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
image,
Point.Empty,
colorBlending,
GraphicsOptions.Default.AlphaCompositionMode,
new GraphicsOptions().AlphaCompositionMode,
opacity));
/// <summary>
@ -100,14 +103,17 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
Image image,
Point location,
float opacity) =>
source.ApplyProcessor(
float opacity)
{
var options = new GraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
GraphicsOptions.Default.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
image,
location,
options.ColorBlendingMode,
options.AlphaCompositionMode,
opacity));
}
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
@ -129,7 +135,7 @@ namespace SixLabors.ImageSharp.Processing
image,
location,
colorBlending,
GraphicsOptions.Default.AlphaCompositionMode,
new GraphicsOptions().AlphaCompositionMode,
opacity));
/// <summary>
@ -172,4 +178,4 @@ namespace SixLabors.ImageSharp.Processing
options.AlphaCompositionMode,
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>
public static IImageProcessingContext
Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) =>
source.Draw(GraphicsOptions.Default, pen, paths);
source.Draw(new GraphicsOptions(), pen, paths);
/// <summary>
/// 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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) =>
source.Draw(GraphicsOptions.Default, pen, path);
source.Draw(new GraphicsOptions(), pen, path);
/// <summary>
/// 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,
IPen pen,
params PointF[] points) =>
source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points)));
source.Draw(new GraphicsOptions(), pen, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// 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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) =>
source.Draw(GraphicsOptions.Default, pen, shape);
source.Draw(new GraphicsOptions(), pen, shape);
/// <summary>
/// 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,
Color color,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, color, location);
source.DrawText(new TextGraphicsOptions(), text, font, color, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing
Font font,
IBrush brush,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, brush, location);
source.DrawText(new TextGraphicsOptions(), text, font, brush, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing
Font font,
IPen pen,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, pen, location);
source.DrawText(new TextGraphicsOptions(), text, font, pen, location);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Processing
IBrush brush,
IPen pen,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location);
source.DrawText(new TextGraphicsOptions(), text, font, brush, pen, location);
/// <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.

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

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
IBrush brush,
Action<PathBuilder> path) =>
source.Fill(GraphicsOptions.Default, brush, path);
source.Fill(new GraphicsOptions(), brush, path);
/// <summary>
/// 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,
IBrush brush,
IPathCollection paths) =>
source.Fill(GraphicsOptions.Default, brush, paths);
source.Fill(new GraphicsOptions(), brush, paths);
/// <summary>
/// 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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
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>
/// 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.
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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) =>
source.Fill(GraphicsOptions.Default, brush);
source.Fill(new GraphicsOptions(), brush);
/// <summary>
/// Flood fills the image with the specified color.
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) =>
source.Fill(GraphicsOptions.Default, brush, region);
source.Fill(new GraphicsOptions(), brush, region);
/// <summary>
/// Flood fills the image with in the region with the specified color.
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options,
IBrush brush,
Region region) =>
source.ApplyProcessor(new FillRegionProcessor(brush, region, options));
source.ApplyProcessor(new FillRegionProcessor(options, brush, region));
/// <summary>
/// Flood fills the image with the specified brush.
@ -90,6 +90,6 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
GraphicsOptions options,
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.
using System;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
@ -38,9 +36,10 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public abstract BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
RectangleF region)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
@ -58,27 +57,24 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="GradientBrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
/// <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="colorStops">An array of color stops sorted by their position.</param>
/// <param name="repetitionMode">Defines if and how the gradient should be repeated.</param>
protected GradientBrushApplicator(
ImageFrame<TPixel> target,
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> target,
ColorStop[] colorStops,
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.repetitionMode = repetitionMode;
}
/// <summary>
/// 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>
/// <inheritdoc/>
internal override TPixel this[int x, int y]
{
get
@ -92,10 +88,10 @@ namespace SixLabors.ImageSharp.Processing
// onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient));
break;
case GradientRepetitionMode.Repeat:
positionOnCompleteGradient = positionOnCompleteGradient % 1;
positionOnCompleteGradient %= 1;
break;
case GradientRepetitionMode.Reflect:
positionOnCompleteGradient = positionOnCompleteGradient % 2;
positionOnCompleteGradient %= 2;
if (positionOnCompleteGradient > 1)
{
positionOnCompleteGradient = 2 - positionOnCompleteGradient;
@ -121,19 +117,8 @@ namespace SixLabors.ImageSharp.Processing
}
else
{
var fromAsVector = from.Color.ToVector4();
var toAsVector = to.Color.ToVector4();
float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio);
// TODO: this should be changeble for different gradienting functions
Vector4 result = PorterDuffFunctions.NormalSrcOver(
fromAsVector,
toAsVector,
onLocalGradient);
TPixel resultColor = default;
resultColor.FromVector4(result);
return resultColor;
return new Color(Vector4.Lerp((Vector4)from.Color, (Vector4)to.Color, onLocalGradient)).ToPixel<TPixel>();
}
}
}
@ -142,8 +127,8 @@ namespace SixLabors.ImageSharp.Processing
/// 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.
/// </summary>
/// <param name="x">The x coordinate of the point</param>
/// <param name="y">The y 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>
/// <returns>
/// The position the given point has on the gradient.
/// 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.
using SixLabors.ImageSharp.PixelFormats;
@ -19,20 +19,22 @@ namespace SixLabors.ImageSharp.Processing
/// Creates the applicator for this brush.
/// </summary>
/// <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="region">The region the brush will be applied to.</param>
/// <param name="options">The graphic options</param>
/// <returns>
/// The brush applicator for this brush
/// The <see cref="BrushApplicator{TPixel}"/> for this brush.
/// </returns>
/// <remarks>
/// The <paramref name="region" /> when being applied to things like shapes would usually be the
/// bounding box of the shape not necessarily the bounds of the whole image
/// bounding box of the shape not necessarily the bounds of the whole image.
/// </remarks>
BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
RectangleF region)
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.
using System;
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@ -32,19 +31,20 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
RectangleF region)
where TPixel : struct, IPixel<TPixel>
{
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>();
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, true);
return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, region, true);
}
/// <summary>
@ -79,21 +79,25 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
private readonly int offsetX;
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator{TPixel}"/> class.
/// </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="image">The image.</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>
public ImageBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> target,
Image<TPixel> image,
RectangleF region,
GraphicsOptions options,
bool shouldDisposeImage)
: base(target, options)
: base(configuration, options, target)
{
this.sourceImage = image;
this.sourceFrame = image.Frames.RootFrame;
@ -104,14 +108,7 @@ namespace SixLabors.ImageSharp.Processing
this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0);
}
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
/// <inheritdoc/>
internal override TPixel this[int x, int y]
{
get
@ -123,14 +120,21 @@ namespace SixLabors.ImageSharp.Processing
}
/// <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 = null;
this.sourceFrame = null;
}
this.sourceImage = null;
this.sourceFrame = null;
this.isDisposed = true;
}
/// <inheritdoc />
@ -140,8 +144,8 @@ namespace SixLabors.ImageSharp.Processing
using (IMemoryOwner<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.Memory.Span;
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
@ -152,13 +156,12 @@ namespace SixLabors.ImageSharp.Processing
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX];
overlaySpan[i] = pixel;
overlaySpan[i] = sourceRow[sourceX];
}
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(
this.sourceFrame.Configuration,
this.Configuration,
destinationRow,
destinationRow,
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.
using System;
@ -39,16 +39,18 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
RectangleF region) =>
new LinearGradientBrushApplicator<TPixel>(
configuration,
options,
source,
this.p1,
this.p2,
this.ColorStops,
this.RepetitionMode,
options);
this.RepetitionMode);
/// <summary>
/// The linear gradient brush applicator.
@ -93,20 +95,22 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="source">The source</param>
/// <param name="start">start point of the gradient</param>
/// <param name="end">end point of the gradient</param>
/// <param name="colorStops">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>
/// <param name="options">the graphics options</param>
/// <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="start">The start point of the gradient.</param>
/// <param name="end">The end point of the gradient.</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(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
PointF start,
PointF end,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode,
GraphicsOptions options)
: base(source, options, colorStops, repetitionMode)
GradientRepetitionMode repetitionMode)
: base(configuration, options, source, colorStops, repetitionMode)
{
this.start = start;
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));
// get and return ratio
float ratio = distance / this.length;
return ratio;
return distance / this.length;
}
}
public override void Dispose()
{
}
}
}
}
}

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

@ -83,12 +83,13 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
RectangleF region)
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)
@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing
"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();
@ -141,10 +142,10 @@ namespace SixLabors.ImageSharp.Processing
Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray();
this.Start = points.First();
this.StartColor = startColor.ToVector4();
this.StartColor = (Vector4)startColor;
this.End = points.Last();
this.EndColor = endColor.ToVector4();
this.EndColor = (Vector4)endColor;
this.length = DistanceBetween(this.End, this.Start);
this.buffer = new PointF[this.path.MaxIntersections];
@ -199,23 +200,25 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="PathGradientBrushApplicator{TPixel}"/> class.
/// </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="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="options">The options.</param>
public PathGradientBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
IList<Edge> edges,
Color centerColor,
GraphicsOptions options)
: base(source, options)
Color centerColor)
: base(configuration, options, source)
{
this.edges = edges;
PointF[] points = edges.Select(s => s.Start).ToArray();
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();
}
@ -232,7 +235,7 @@ namespace SixLabors.ImageSharp.Processing
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);
@ -250,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing
float length = DistanceBetween(intersection, this.center);
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>();
}
@ -277,11 +280,6 @@ namespace SixLabors.ImageSharp.Processing
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.
using System;
@ -61,8 +61,8 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="pattern">The pattern.</param>
internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix<bool> pattern)
{
var foreColorVector = foreColor.ToVector4();
var backColorVector = backColor.ToVector4();
var foreColorVector = (Vector4)foreColor;
var backColorVector = (Vector4)backColor;
this.pattern = new DenseMatrix<Color>(pattern.Columns, pattern.Rows);
this.patternVector = new DenseMatrix<Vector4>(pattern.Columns, pattern.Rows);
for (int i = 0; i < pattern.Data.Length; i++)
@ -92,14 +92,16 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
RectangleF region)
where TPixel : struct, IPixel<TPixel> =>
new PatternBrushApplicator<TPixel>(
configuration,
options,
source,
this.pattern.ToPixelMatrix<TPixel>(source.Configuration),
options);
this.pattern.ToPixelMatrix<TPixel>(configuration));
/// <summary>
/// The pattern brush applicator.
@ -115,41 +117,33 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator{TPixel}" /> class.
/// </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="pattern">The pattern.</param>
/// <param name="options">The options</param>
public PatternBrushApplicator(ImageFrame<TPixel> source, in DenseMatrix<TPixel> pattern, GraphicsOptions options)
: base(source, options)
public PatternBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
in DenseMatrix<TPixel> pattern)
: base(configuration, options, source)
{
this.pattern = pattern;
}
/// <summary>
/// Gets the color for a single pixel.
/// </summary>#
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The Color.
/// </returns>
/// <inheritdoc/>
internal override TPixel this[int x, int y]
{
get
{
x = x % this.pattern.Columns;
y = y % this.pattern.Rows;
x %= this.pattern.Columns;
y %= this.pattern.Rows;
// 2d array index at row/column
return this.pattern[y, x];
}
}
/// <inheritdoc />
public override void Dispose()
{
// noop
}
/// <inheritdoc />
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<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.Memory.Span;
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;
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);
this.Blender.Blend(
this.Target.Configuration,
this.Configuration,
destinationRow,
destinationRow,
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>
/// Initializes a new instance of the <see cref="FillProcessor"/> class.
/// </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>
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.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;
Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
IBrush brush = this.definition.Brush;
GraphicsOptions options = this.definition.Options;
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration)
.MultiplyMinimumPixelsPerTask(4);
var colorPixel = solidBrush.Color.ToPixel<TPixel>();
TPixel colorPixel = solidBrush.Color.ToPixel<TPixel>();
ParallelHelper.IterateRows(
workingRect,
@ -84,11 +84,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = brush.CreateApplicator(
configuration,
options,
source,
sourceRectangle,
options))
sourceRectangle))
{
amount.GetSpan().Fill(1f);
amount.Memory.Span.Fill(1f);
ParallelHelper.IterateRows(
workingRect,
@ -100,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int offsetY = y - startY;
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>
/// Initializes a new instance of the <see cref="FillRegionProcessor" /> class.
/// </summary>
/// <param name="options">The graphics options.</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="options">The configuration options.</param>
public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options)
public FillRegionProcessor(GraphicsOptions options, IBrush brush, Region region)
{
this.Region = region;
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;
using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
@ -81,8 +81,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
Span<float> buffer = bBuffer.Memory.Span;
Span<float> scanline = bScanline.Memory.Span;
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush);
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
};
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;
var renderer = new TextRenderer(this.textRenderer);
renderer.RenderText(this.Text, style);
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
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)
{
@ -326,6 +326,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
float subpixelFraction = 1f / 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++)
{
@ -337,8 +339,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
var start = new PointF(path.Bounds.Left - 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);
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.
using System;
@ -9,7 +9,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.
/// A radial gradient brush, defined by center point and radius.
/// </summary>
public sealed class RadialGradientBrush : GradientBrush
{
@ -35,12 +35,14 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
RectangleF region) =>
new RadialGradientBrushApplicator<TPixel>(
source,
configuration,
options,
source,
this.center,
this.radius,
this.ColorStops,
@ -57,30 +59,27 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="target">The target image</param>
/// <param name="options">The options.</param>
/// <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="center">Center point of the gradient.</param>
/// <param name="radius">Radius of the gradient.</param>
/// <param name="colorStops">Definition of colors.</param>
/// <param name="repetitionMode">How the colors are repeated beyond the first gradient.</param>
public RadialGradientBrushApplicator(
ImageFrame<TPixel> target,
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> target,
PointF center,
float radius,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
: base(configuration, options, target, colorStops, repetitionMode)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="Dispose" />
public override void Dispose()
{
}
/// <summary>
/// As this is a circular gradient, the position on the gradient is based on
/// the distance of the point to the center.
@ -90,6 +89,7 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>the position on the color gradient.</returns>
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));
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.
using System;
using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
@ -38,9 +37,6 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Gets the source color.
/// </summary>
/// <value>
/// The color of the source.
/// </value>
public Color SourceColor { get; }
/// <summary>
@ -50,17 +46,19 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
RectangleF region)
where TPixel : struct, IPixel<TPixel>
{
return new RecolorBrushApplicator<TPixel>(
configuration,
options,
source,
this.SourceColor.ToPixel<TPixel>(),
this.TargetColor.ToPixel<TPixel>(),
this.Threshold,
options);
this.Threshold);
}
/// <summary>
@ -74,11 +72,6 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
private readonly Vector4 sourceColor;
/// <summary>
/// The target color.
/// </summary>
private readonly Vector4 targetColor;
/// <summary>
/// The threshold.
/// </summary>
@ -89,16 +82,22 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrushApplicator{TPixel}" /> class.
/// </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="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param>
/// <param name="options">The options</param>
public RecolorBrushApplicator(ImageFrame<TPixel> source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options)
: base(source, options)
public RecolorBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
TPixel sourceColor,
TPixel targetColor,
float threshold)
: base(configuration, options, source)
{
this.sourceColor = sourceColor.ToVector4();
this.targetColor = targetColor.ToVector4();
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 :)
@ -109,14 +108,7 @@ namespace SixLabors.ImageSharp.Processing
this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold;
}
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>
/// The color
/// </returns>
/// <inheritdoc />
internal override TPixel this[int x, int y]
{
get
@ -138,11 +130,6 @@ namespace SixLabors.ImageSharp.Processing
}
}
/// <inheritdoc />
public override void Dispose()
{
}
/// <inheritdoc />
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<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();
Span<float> amountSpan = amountBuffer.Memory.Span;
Span<TPixel> overlaySpan = overlay.Memory.Span;
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);
this.Blender.Blend(
this.Target.Configuration,
this.Configuration,
destinationRow,
destinationRow,
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.
using System;
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
@ -17,33 +16,29 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
public class SolidBrush : IBrush
{
/// <summary>
/// The color to paint.
/// </summary>
private readonly Color color;
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrush"/> class.
/// </summary>
/// <param name="color">The color.</param>
public SolidBrush(Color color)
{
this.color = color;
this.Color = color;
}
/// <summary>
/// Gets the color.
/// </summary>
/// <value>
/// The color.
/// </value>
public Color Color => this.color;
public Color Color { get; }
/// <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>
{
return new SolidBrushApplicator<TPixel>(source, this.color.ToPixel<TPixel>(), options);
return new SolidBrushApplicator<TPixel>(configuration, options, source, this.Color.ToPixel<TPixel>());
}
/// <summary>
@ -52,38 +47,49 @@ namespace SixLabors.ImageSharp.Processing
private class SolidBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator{TPixel}"/> class.
/// </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="color">The color.</param>
/// <param name="options">The options</param>
public SolidBrushApplicator(ImageFrame<TPixel> source, TPixel color, GraphicsOptions options)
: base(source, options)
public SolidBrushApplicator(
Configuration configuration,
GraphicsOptions options,
ImageFrame<TPixel> source,
TPixel color)
: base(configuration, options, source)
{
this.Colors = source.MemoryAllocator.Allocate<TPixel>(source.Width);
this.Colors.GetSpan().Fill(color);
this.Colors.Memory.Span.Fill(color);
}
/// <summary>
/// Gets the colors.
/// </summary>
protected IMemoryOwner<TPixel> Colors { get; }
protected IMemoryOwner<TPixel> Colors { get; private set; }
/// <summary>
/// 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] => this.Colors.GetSpan()[x];
/// <inheritdoc/>
internal override TPixel this[int x, int y] => this.Colors.Memory.Span[x];
/// <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 />
@ -102,17 +108,17 @@ namespace SixLabors.ImageSharp.Processing
}
MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;
Configuration configuration = this.Target.Configuration;
Configuration configuration = this.Configuration;
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
{
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++)
{
@ -123,11 +129,11 @@ namespace SixLabors.ImageSharp.Processing
configuration,
destinationRow,
destinationRow,
this.Colors.GetSpan(),
this.Colors.Memory.Span,
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.
using SixLabors.Fonts;
@ -9,120 +9,169 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Options for influencing the drawing functions.
/// </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>
/// Represents the default <see cref="TextGraphicsOptions"/>.
/// Initializes a new instance of the <see cref="TextGraphicsOptions"/> class.
/// </summary>
public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true);
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;
public TextGraphicsOptions()
{
}
/// <summary>
/// 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)
private TextGraphicsOptions(TextGraphicsOptions source)
{
this.applyKerning = true;
this.tabWidth = 4;
this.WrapTextWidth = 0;
this.horizontalAlignment = HorizontalAlignment.Left;
this.verticalAlignment = VerticalAlignment.Top;
this.antialiasSubpixelDepth = 16;
this.ColorBlendingMode = PixelColorBlendingMode.Normal;
this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1;
this.antialias = enableAntialiasing;
this.dpiX = DefaultTextDpi;
this.dpiY = DefaultTextDpi;
this.AlphaCompositionMode = source.AlphaCompositionMode;
this.Antialias = source.Antialias;
this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth;
this.ApplyKerning = source.ApplyKerning;
this.BlendPercentage = source.BlendPercentage;
this.ColorBlendingMode = source.ColorBlendingMode;
this.DpiX = source.DpiX;
this.DpiY = source.DpiY;
this.HorizontalAlignment = source.HorizontalAlignment;
this.TabWidth = source.TabWidth;
this.WrapTextWidth = source.WrapTextWidth;
this.VerticalAlignment = source.VerticalAlignment;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// Defaults to true.
/// </summary>
public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; }
public bool Antialias { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </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>
/// 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>
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
// or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1.
set
{
Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage));
this.blendPercentage = value;
}
}
/// <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>
public PixelColorBlendingMode ColorBlendingMode { get; set; }
public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal;
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// Defaults to <see cref= "PixelAlphaCompositionMode.SrcOver" />.
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; }
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver;
/// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
/// Defaults to true;
/// </summary>
public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; }
public bool ApplyKerning { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating the number of space widths a tab should lock to.
/// Defaults to 4.
/// </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>
/// 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>
public float WrapTextWidth { get; set; }
/// <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>
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>
/// 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>
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>
/// 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
/// 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.
/// Defaults to <see cref="HorizontalAlignment.Left"/>.
/// </summary>
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; }
public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Left;
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// Defaults to <see cref="VerticalAlignment.Top"/>.
/// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; }
public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Top;
/// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.
@ -133,8 +182,9 @@ namespace SixLabors.ImageSharp.Processing
/// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{
return new TextGraphicsOptions(options.Antialias)
return new TextGraphicsOptions()
{
Antialias = options.Antialias,
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage,
ColorBlendingMode = options.ColorBlendingMode,
@ -151,13 +201,17 @@ namespace SixLabors.ImageSharp.Processing
/// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{
return new GraphicsOptions(options.Antialias)
return new GraphicsOptions()
{
Antialias = options.Antialias,
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode,
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.
using System;

35
src/ImageSharp/Color/Color.cs

@ -133,8 +133,7 @@ namespace SixLabors.ImageSharp
public override string ToString() => this.ToHex();
/// <summary>
/// Converts the color instance to an <see cref="IPixel{TSelf}"/>
/// implementation defined by <typeparamref name="TPixel"/>.
/// Converts the color instance to a specified <see cref="IPixel{TSelf}"/> type.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <returns>The pixel value.</returns>
@ -147,6 +146,24 @@ namespace SixLabors.ImageSharp
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 />
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Color other)
@ -166,19 +183,5 @@ namespace SixLabors.ImageSharp
{
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.
using SixLabors.ImageSharp.PixelFormats;
@ -8,170 +8,82 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct GraphicsOptions
public class GraphicsOptions : IDeepCloneable<GraphicsOptions>
{
/// <summary>
/// Represents the default <see cref="GraphicsOptions"/>.
/// </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;
}
private int antialiasSubpixelDepth = 16;
private float blendPercentage = 1F;
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// Initializes a new instance of the <see cref="GraphicsOptions"/> class.
/// </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>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
public GraphicsOptions()
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
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>
/// <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)
private GraphicsOptions(GraphicsOptions source)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = composition;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
this.AlphaCompositionMode = source.AlphaCompositionMode;
this.Antialias = source.Antialias;
this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth;
this.BlendPercentage = source.BlendPercentage;
this.ColorBlendingMode = source.ColorBlendingMode;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// Defaults to true.
/// </summary>
public bool Antialias
{
get => this.antialias ?? true;
set => this.antialias = value;
}
public bool Antialias { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// Defaults to 16.
/// </summary>
public int AntialiasSubpixelDepth
{
get => this.antialiasSubpixelDepth ?? 16;
set => this.antialiasSubpixelDepth = value;
get
{
return this.antialiasSubpixelDepth;
}
set
{
Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth));
this.antialiasSubpixelDepth = value;
}
}
/// <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>
public float BlendPercentage
{
get => (this.blendPercentage ?? 1).Clamp(0, 1);
set => this.blendPercentage = value;
}
get
{
return this.blendPercentage;
}
// In the future we could expose a PixelBlender<TPixel> directly on here
// or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1.
set
{
Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage));
this.blendPercentage = value;
}
}
/// <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>
public PixelColorBlendingMode ColorBlendingMode
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
}
public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal;
/// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// Defaults to <see cref="PixelAlphaCompositionMode.SrcOver"/>.
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode
{
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
}
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver;
/// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </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;
}
/// <inheritdoc/>
public GraphicsOptions DeepClone() => new GraphicsOptions(this);
}
}
}

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.
using System.Buffers;
@ -11,8 +11,18 @@ namespace SixLabors.ImageSharp.Memory
/// <summary>
/// Extension methods for <see cref="MemoryAllocator"/>.
/// </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>(
this MemoryAllocator memoryAllocator,
int width,
@ -26,6 +36,15 @@ namespace SixLabors.ImageSharp.Memory
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>(
this MemoryAllocator memoryAllocator,
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="paddingInBytes">The padding</param>
/// <returns>A <see cref="IManagedByteBuffer"/></returns>
public static IManagedByteBuffer AllocatePaddedPixelRowBuffer(
internal static IManagedByteBuffer AllocatePaddedPixelRowBuffer(
this MemoryAllocator memoryAllocator,
int width,
int pixelSizeInBytes,
@ -51,4 +70,4 @@ namespace SixLabors.ImageSharp.Memory
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.
using System;
@ -98,9 +98,9 @@ namespace SixLabors.ImageSharp.Primitives
}
/// <summary>
/// Gets a Span wrapping the Data.
/// Gets a span wrapping the <see cref="Data"/>.
/// </summary>
internal Span<T> Span => new Span<T>(this.Data);
public Span<T> Span => new Span<T>(this.Data);
/// <summary>
/// Gets or sets the item at the specified position.
@ -222,4 +222,4 @@ namespace SixLabors.ImageSharp.Primitives
/// <inheritdoc/>
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.
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>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) =>
BackgroundColor(source, GraphicsOptions.Default, color);
BackgroundColor(source, new GraphicsOptions(), color);
/// <summary>
/// Replaces the background color of image with the given one.
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
Color color,
Rectangle rectangle) =>
BackgroundColor(source, GraphicsOptions.Default, color, rectangle);
BackgroundColor(source, new GraphicsOptions(), color, rectangle);
/// <summary>
/// Replaces the background color of image with the given one.
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
GraphicsOptions options,
Color color) =>
source.ApplyProcessor(new BackgroundColorProcessor(color, options));
source.ApplyProcessor(new BackgroundColorProcessor(options, color));
/// <summary>
/// Replaces the background color of image with the given one.
@ -64,6 +64,6 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options,
Color color,
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.
using SixLabors.ImageSharp.Primitives;
@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source) =>
Glow(source, GraphicsOptions.Default);
Glow(source, new GraphicsOptions());
/// <summary>
/// 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>
public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color)
{
return Glow(source, GraphicsOptions.Default, color);
return Glow(source, new GraphicsOptions(), color);
}
/// <summary>
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="radius">The the radius.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) =>
Glow(source, GraphicsOptions.Default, radius);
Glow(source, new GraphicsOptions(), radius);
/// <summary>
/// Applies a radial glow effect to an image.
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) =>
source.Glow(GraphicsOptions.Default, rectangle);
source.Glow(new GraphicsOptions(), rectangle);
/// <summary>
/// Applies a radial glow effect to an image.
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing
Color color,
float radius,
Rectangle rectangle) =>
source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle);
source.Glow(new GraphicsOptions(), color, ValueSize.Absolute(radius), rectangle);
/// <summary>
/// Applies a radial glow effect to an image.
@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Processing
Color color,
ValueSize radius,
Rectangle rectangle) =>
source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle);
source.ApplyProcessor(new GlowProcessor(options, color, radius), rectangle);
/// <summary>
/// Applies a radial glow effect to an image.
@ -170,6 +170,6 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options,
Color color,
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.
using SixLabors.ImageSharp.Primitives;
@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source) =>
Vignette(source, GraphicsOptions.Default);
Vignette(source, new GraphicsOptions());
/// <summary>
/// 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>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) =>
Vignette(source, GraphicsOptions.Default, color);
Vignette(source, new GraphicsOptions(), color);
/// <summary>
/// Applies a radial vignette effect to an image.
@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
float radiusX,
float radiusY) =>
Vignette(source, GraphicsOptions.Default, radiusX, radiusY);
Vignette(source, new GraphicsOptions(), radiusX, radiusY);
/// <summary>
/// Applies a radial vignette effect to an image.
@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) =>
Vignette(source, GraphicsOptions.Default, rectangle);
Vignette(source, new GraphicsOptions(), rectangle);
/// <summary>
/// Applies a radial vignette effect to an image.
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing
float radiusX,
float radiusY,
Rectangle rectangle) =>
source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle);
source.Vignette(new GraphicsOptions(), color, radiusX, radiusY, rectangle);
/// <summary>
/// Applies a radial vignette effect to an image.
@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Processing
ValueSize radiusX,
ValueSize radiusY,
Rectangle rectangle) =>
source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle);
source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY), rectangle);
private static IImageProcessingContext VignetteInternal(
this IImageProcessingContext source,
@ -174,6 +174,6 @@ namespace SixLabors.ImageSharp.Processing
Color color,
ValueSize radiusX,
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>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor"/> class.
/// </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>
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.GraphicsOptions = options;

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

@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor" /> class.
/// </summary>
/// <param name="color">The color or the glow.</param>
/// <param name="options">The options effecting blending and composition.</param>
public GlowProcessor(Color color, GraphicsOptions options)
: this(color, 0, options)
/// <param name="color">The color or the glow.</param>
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="radius">The radius of the glow.</param>
internal GlowProcessor(Color color, ValueSize radius)
: this(color, radius, GraphicsOptions.Default)
: this(new GraphicsOptions(), color, radius)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor" /> class.
/// </summary>
/// <param name="options">The options effecting blending and composition.</param>
/// <param name="color">The color or the glow.</param>
/// <param name="radius">The radius of the glow.</param>
/// <param name="options">The options effecting blending and composition.</param>
internal GlowProcessor(Color color, ValueSize radius, GraphicsOptions options)
internal GlowProcessor(GraphicsOptions options, Color color, ValueSize radius)
{
this.GlowColor = color;
this.Radius = radius;
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary>
/// Gets the the radius.
/// </summary>
internal ValueSize Radius { get; }
internal ValueSize Radius { get; }
/// <inheritdoc />
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>
/// <param name="color">The color of the vignette.</param>
public VignetteProcessor(Color color)
: this(color, GraphicsOptions.Default)
: this(new GraphicsOptions(), color)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="VignetteProcessor" /> class.
/// </summary>
/// <param name="color">The color of the vignette.</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.GraphicsOptions = options;
@ -35,11 +35,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary>
/// Initializes a new instance of the <see cref="VignetteProcessor" /> class.
/// </summary>
/// <param name="options">The options effecting blending and composition.</param>
/// <param name="color">The color of the vignette.</param>
/// <param name="radiusX">The x-radius.</param>
/// <param name="radiusY">The y-radius.</param>
/// <param name="options">The options effecting blending and composition.</param>
internal VignetteProcessor(Color color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options)
internal VignetteProcessor(GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY)
{
this.VignetteColor = color;
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.
using System.Drawing;
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Benchmarks
graphics.SmoothingMode = SmoothingMode.AntiAlias;
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))
{
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))
{
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(
@ -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.
using System.Drawing;
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks
[Params(10, 100)]
public int TextIterations { get; set; }
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")]
public void DrawTextSystemDrawing()
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (var font = new Font("Arial", 12, GraphicsUnit.Point))
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);
}
}
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks
using (var image = new Image<Rgba32>(800, 800))
{
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(
x => DrawTextOldVersion(
x,
new TextGraphicsOptions(true) { WrapTextWidth = 780 },
TextToRender,
new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 },
this.TextToRender,
font,
null,
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()
{
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.
using System;
@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = new Pen(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
}
[Theory]
[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)
@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.Dash(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
}
[Theory]
[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)
@ -46,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.Dot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
}
[Theory]
[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)
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.DashDot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
}
@ -68,11 +68,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.DashDotDot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
}
private static void DrawLinesImpl<TPixel>(
TestImageProvider<TPixel> provider,
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) };
GraphicsOptions options = new GraphicsOptions(antialias);
GraphicsOptions options = new GraphicsOptions { Antialias = antialias };
string aa = antialias ? "" : "_NoAntialias";
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);
GraphicsOptions options = new GraphicsOptions(antialias);
GraphicsOptions options = new GraphicsOptions { Antialias = antialias };
string aa = antialias ? "" : "_NoAntialias";
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.
using System;
@ -16,6 +16,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using SixLabors.Shapes;
[GroupOutput("Drawing/GradientBrushes")]
public class FillLinearGradientBrushTests
@ -392,5 +394,44 @@ namespace SixLabors.ImageSharp.Tests.Drawing
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);
var options = new GraphicsOptions(antialias);
var options = new GraphicsOptions { Antialias = antialias };
string aa = antialias ? "" : "_NoAntialias";
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.
using System.Numerics;
@ -16,8 +16,6 @@ using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class FillRegionProcessorTests
{
@ -35,11 +33,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
var brush = new Mock<IBrush>();
var region = new MockRegion2(bounds);
var options = new GraphicsOptions(antialias)
var options = new GraphicsOptions
{
Antialias = antialias,
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);
processor.Execute(img, bounds);
@ -51,8 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
var bounds = new Rectangle(-100, -10, 10, 10);
var brush = new Mock<IBrush>();
var options = new GraphicsOptions(true);
var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options);
var options = new GraphicsOptions { Antialias = true };
var processor = new FillRegionProcessor(options, brush.Object, new MockRegion1());
var img = new Image<Rgba32>(10, 10);
processor.Execute(img, bounds);
}
@ -73,11 +72,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
public void DoesNotThrowForIssue928()
{
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(ctx => {
img.Mutate(ctx =>
{
ctx.DrawLines(
Rgba32.Red,
0.984252f,
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new PointF(104.782608f, 1075.13245f),
new PointF(104.782608f, 1075.13245f)
);
}
}
);
}
}
@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
[Fact]
public void DoesNotThrowFillingTriangle()
{
using(var image = new Image<Rgba32>(28, 28))
using (var image = new Image<Rgba32>(28, 28))
{
var path = new Polygon(
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];
var options = new GraphicsOptions(false)
{
ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage
};
var options = new GraphicsOptions
{
Antialias = false,
ColorBlendingMode = blenderMode,
BlendPercentage = blendPercentage
};
if (triggerFillRegion)
{
@ -173,13 +175,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}
var testOutputDetails = new
{
triggerFillRegion = triggerFillRegion,
newColorName = newColorName,
alpha = alpha,
blenderMode = blenderMode,
blendPercentage = blendPercentage
};
{
triggerFillRegion = triggerFillRegion,
newColorName = newColorName,
alpha = alpha,
blenderMode = blenderMode,
blendPercentage = blendPercentage
};
image.DebugSave(
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.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes;
using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
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;
Pen pen = Pens.Solid(Rgba32.HotPink, 1);
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);
Assert.Equal(GraphicsOptions.Default, processor.Options);
Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
@ -60,13 +63,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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++)
{
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);
Assert.IsType<ComplexPolygon>(region.Shape);
@ -84,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
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);
Assert.IsType<ComplexPolygon>(region.Shape);
@ -97,13 +100,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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++)
{
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);
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.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes;
using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
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;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
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);
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);
@ -44,10 +47,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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>();
Assert.Equal(this.noneDefault, processor.Options);
Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -62,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
this.operations.Fill(this.color, this.path);
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);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -75,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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>();
Assert.Equal(this.noneDefault, processor.Options);
Assert.Equal(this.nonDefault, processor.Options);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
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.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes;
using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
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;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
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);
Assert.Equal(GraphicsOptions.Default, processor.Options);
Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
@ -61,13 +64,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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++)
{
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);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -86,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
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);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -100,13 +103,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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++)
{
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);
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.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Shapes;
using Xunit;
@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
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;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
SixLabors.Primitives.PointF[] path = {
@ -32,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
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);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -44,10 +47,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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>();
Assert.Equal(this.noneDefault, processor.Options);
Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -63,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
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);
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
@ -76,10 +79,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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>();
Assert.Equal(this.noneDefault, processor.Options);
Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
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.
using SixLabors.ImageSharp.PixelFormats;
@ -6,17 +6,19 @@ using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.ImageSharp.Tests.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
public class FillRectangle : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76);
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
private GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false };
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]
public void CorrectlySetsBrushAndRectangle()
@ -24,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
this.operations.Fill(this.brush, this.rectangle);
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);
Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape);
@ -39,10 +41,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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>();
Assert.Equal(this.noneDefault, processor.Options);
Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
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);
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);
Shapes.RectangularPolygon rect = Assert.IsType<Shapes.RectangularPolygon>(region.Shape);
@ -76,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
[Fact]
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>();
Assert.Equal(this.noneDefault, processor.Options);
Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer);
ShapeRegion region = Assert.IsType<ShapeRegion>(processor.Region);
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.
using System;
using System.Collections.Generic;
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}
}
}
[Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
@ -46,7 +46,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
Color.DarkBlue,
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,
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)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.Transparent,
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)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink,
new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY)));
@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition },
transparentRed,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
);
@ -130,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
PixelAlphaCompositionMode composition)
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 scaleY = (dstImg.Height / 100);
@ -146,13 +147,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
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);
}
}
private static void VerifyImage<TPixel>(
TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending,
@ -165,13 +166,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
new { composition, blending },
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
var comparer = ImageComparer.TolerantPercentage(0.01f, 3);
img.CompareFirstFrameToReferenceOutput(comparer,
provider,
new { composition, blending },
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()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
new TextGraphicsOptions { Antialias = true },
"123",
this.Font,
Brushes.Solid(Color.Red),
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact]
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);
}
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact]
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);
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
public void DrawForEachACharacterWhenPenSetAndNotBrush()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
new TextGraphicsOptions { Antialias = true },
"123",
this.Font,
null,
@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact]
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);
}
@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
public void DrawForEachACharacterWhenPenSetAndFillFroEachWhenBrushSet()
{
this.operations.DrawText(
new TextGraphicsOptions(true),
new TextGraphicsOptions { Antialias = true },
"123",
this.Font,
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 center = new PointF(img.Width / 2, img.Height / 2);
var textGraphicOptions = new TextGraphicsOptions(true)
var textGraphicOptions = new TextGraphicsOptions
{
Antialias = true,
HorizontalAlignment = HorizontalAlignment.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",
20);
var textOptions = new TextGraphicsOptions(true) { WrapTextWidth = 1000 };
var textOptions = new TextGraphicsOptions
{
Antialias = true,
WrapTextWidth = 1000
};
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.
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
@ -9,16 +11,184 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
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]
public void ExplicitCastOfGraphicsOptions()
{
var opt = new GraphicsOptions(false)
TextGraphicsOptions textOptions = new GraphicsOptions
{
Antialias = false,
AntialiasSubpixelDepth = 99
};
TextGraphicsOptions textOptions = opt;
Assert.False(textOptions.Antialias);
Assert.Equal(99, textOptions.AntialiasSubpixelDepth);
}
@ -26,8 +196,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
[Fact]
public void ImplicitCastToGraphicsOptions()
{
var textOptions = new TextGraphicsOptions(false)
var textOptions = new TextGraphicsOptions
{
Antialias = false,
AntialiasSubpixelDepth = 99
};
@ -37,4 +208,4 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
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.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
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]
public void IsOpaqueColor()
{
Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Transparent));
Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.True(new GraphicsOptions().IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions { BlendPercentage = .5F }.IsOpaqueColorWithoutBlending(Rgba32.Red));
Assert.False(new GraphicsOptions().IsOpaqueColorWithoutBlending(Rgba32.Transparent));
Assert.False(new GraphicsOptions { ColorBlendingMode = PixelColorBlendingMode.Lighten, BlendPercentage = 1F }.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)
{
context.DrawLines(
new GraphicsOptions(false),
new GraphicsOptions { Antialias = false },
Color.Black,
1,
new PointF(i, 0.1066f),
new PointF(i, 10.1066f));
context.DrawLines(
new GraphicsOptions(false),
new GraphicsOptions { Antialias = false },
Color.Red,
1,
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.
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> dest = provider.GetImage())
{
GraphicsOptions options = new GraphicsOptions
{
AlphaCompositionMode = mode
var options = new GraphicsOptions
{
Antialias = false,
AlphaCompositionMode = mode
};
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()
{
this.options = new GraphicsOptions(false);
this.options = new GraphicsOptions { Antialias = false };
this.source = new Image<Rgba32>(91 + 324, 123 + 56);
this.rect = new Rectangle(91, 123, 324, 56); // make this random?
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.
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Effects
{
public class BackgroundColorTest : BaseImageOperationsExtensionTest
{
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
[Fact]
public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet()
{
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);
}
@ -24,9 +26,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet()
{
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);
}
@ -34,9 +36,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet()
{
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);
}
@ -44,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects
public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet()
{
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);
}
}
}
}

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.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Primitives;
using Xunit;
@ -12,13 +13,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{
public class GlowTest : BaseImageOperationsExtensionTest
{
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
[Fact]
public void Glow_GlowProcessorWithDefaultValues()
{
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(ValueSize.PercentageOfWidth(.5f), p.Radius);
}
@ -27,9 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Glow_Color_GlowProcessorWithDefaultValues()
{
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(ValueSize.PercentageOfWidth(.5f), p.Radius);
}
@ -38,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Glow_Radux_GlowProcessorWithDefaultValues()
{
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(ValueSize.Absolute(3.5f), p.Radius);
}
@ -50,11 +53,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{
var rect = new Rectangle(12, 123, 43, 65);
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(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.
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Primitives;
using Xunit;
@ -11,13 +12,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
{
public class VignetteTest : BaseImageOperationsExtensionTest
{
private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer();
[Fact]
public void Vignette_VignetteProcessorWithDefaultValues()
{
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(ValueSize.PercentageOfWidth(.5f), p.RadiusX);
Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY);
@ -27,9 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Vignette_Color_VignetteProcessorWithDefaultValues()
{
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(ValueSize.PercentageOfWidth(.5f), p.RadiusX);
Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY);
@ -39,9 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays
public void Vignette_Radux_VignetteProcessorWithDefaultValues()
{
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(ValueSize.Absolute(3.5f), p.RadiusX);
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);
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(ValueSize.PercentageOfWidth(.5f), p.RadiusX);
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();
}
}
Loading…
Cancel
Save