Browse Source

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

# Conflicts:
#	tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
pull/909/head
Brian Popow 7 years ago
parent
commit
3da351a24b
  1. 2
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  2. 2
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings
  3. 2
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  4. 120
      src/ImageSharp.Drawing/Processing/Brushes.cs
  5. 10
      src/ImageSharp.Drawing/Processing/ColorStop.cs
  6. 137
      src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs
  7. 22
      src/ImageSharp.Drawing/Processing/DrawingHelpers.cs
  8. 21
      src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs
  9. 61
      src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs
  10. 175
      src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs
  11. 61
      src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs
  12. 57
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs
  13. 58
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs
  14. 61
      src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs
  15. 58
      src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs
  16. 94
      src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs
  17. 36
      src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs
  18. 36
      src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs
  19. 33
      src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs
  20. 38
      src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs
  21. 34
      src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs
  22. 54
      src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs
  23. 42
      src/ImageSharp.Drawing/Processing/GradientBrushBase.cs
  24. 6
      src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs
  25. 13
      src/ImageSharp.Drawing/Processing/IBrush.cs
  26. 17
      src/ImageSharp.Drawing/Processing/IPen.cs
  27. 73
      src/ImageSharp.Drawing/Processing/ImageBrush.cs
  28. 32
      src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs
  29. 36
      src/ImageSharp.Drawing/Processing/PatternBrush.cs
  30. 26
      src/ImageSharp.Drawing/Processing/Pen.cs
  31. 50
      src/ImageSharp.Drawing/Processing/Pens.cs
  32. 108
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  33. 111
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  34. 123
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  35. 118
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
  36. 192
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  37. 195
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
  38. 430
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  39. 446
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
  40. 21
      src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs
  41. 30
      src/ImageSharp.Drawing/Processing/RecolorBrush.cs
  42. 22
      src/ImageSharp.Drawing/Processing/SolidBrush.cs
  43. 328
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  44. 17
      src/ImageSharp/Color/Color.Conversions.cs
  45. 12
      src/ImageSharp/Color/Color.cs
  46. 232
      src/ImageSharp/GraphicsOptions.cs
  47. 17
      src/ImageSharp/Image.cs
  48. 2
      src/ImageSharp/ImageSharp.csproj
  49. 11
      src/ImageSharp/Image{TPixel}.cs
  50. 284
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs
  51. 14
      src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs
  52. 2
      tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
  53. 2
      tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
  54. 2
      tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
  55. 12
      tests/ImageSharp.Benchmarks/Drawing/DrawText.cs
  56. 24
      tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs
  57. 10
      tests/ImageSharp.Benchmarks/Samplers/Resize.cs
  58. 11
      tests/ImageSharp.Tests/Color/ColorTests.cs
  59. 92
      tests/ImageSharp.Tests/Drawing/BeziersTests.cs
  60. 48
      tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs
  61. 70
      tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs
  62. 100
      tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs
  63. 133
      tests/ImageSharp.Tests/Drawing/DrawPathTests.cs
  64. 43
      tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs
  65. 62
      tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs
  66. 36
      tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs
  67. 21
      tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs
  68. 107
      tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs
  69. 8
      tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs
  70. 155
      tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs
  71. 14
      tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs
  72. 11
      tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs
  73. 22
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs
  74. 201
      tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs
  75. 193
      tests/ImageSharp.Tests/Drawing/LineTests.cs
  76. 16
      tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs
  77. 16
      tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs
  78. 16
      tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs
  79. 16
      tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs
  80. 16
      tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs
  81. 102
      tests/ImageSharp.Tests/Drawing/PolygonTests.cs
  82. 48
      tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs
  83. 52
      tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs
  84. 2
      tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs
  85. 109
      tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs
  86. 26
      tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs
  87. 240
      tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
  88. 70
      tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
  89. 12
      tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
  90. 49
      tests/ImageSharp.Tests/ImageOperationTests.cs
  91. 7
      tests/ImageSharp.Tests/Issues/Issue412.cs
  92. 19
      tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs
  93. 15
      tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs
  94. 6
      tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs
  95. 4
      tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs
  96. 4
      tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs
  97. 4
      tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs
  98. 3
      tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs
  99. 9
      tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs
  100. 6
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

2
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -39,9 +39,9 @@
<ItemGroup>
<AdditionalFiles Include="..\..\standards\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta0008" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0008" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-rc.114" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup>

2
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj.DotSettings

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cextensions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

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

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Gets the blend percentage
/// </summary>
protected GraphicsOptions Options { get; private set; }
protected GraphicsOptions Options { get; }
/// <summary>
/// Gets the color for a single pixel.

120
src/ImageSharp.Drawing/Processing/Brushes.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// A collection of methods for creating generic brushes.
/// </summary>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
/// <returns>A New <see cref="PatternBrush"/></returns>
public static class Brushes
{
/// <summary>
@ -94,163 +94,131 @@ namespace SixLabors.ImageSharp.Processing
/// Create as brush that will paint a solid color
/// </summary>
/// <param name="color">The color.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static SolidBrush<TPixel> Solid<TPixel>(TPixel color)
where TPixel : struct, IPixel<TPixel>
=> new SolidBrush<TPixel>(color);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static SolidBrush Solid(Color color) => new SolidBrush(color);
/// <summary>
/// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Percent10<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, Percent10Pattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent10(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, Percent10Pattern);
/// <summary>
/// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Percent10<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, Percent10Pattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent10(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, Percent10Pattern);
/// <summary>
/// Create as brush that will paint a Percent20 Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Percent20<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, Percent20Pattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent20(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, Percent20Pattern);
/// <summary>
/// Create as brush that will paint a Percent20 Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Percent20<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, Percent20Pattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Percent20(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, Percent20Pattern);
/// <summary>
/// Create as brush that will paint a Horizontal Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Horizontal<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, HorizontalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Horizontal(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, HorizontalPattern);
/// <summary>
/// Create as brush that will paint a Horizontal Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Horizontal<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, HorizontalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Horizontal(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, HorizontalPattern);
/// <summary>
/// Create as brush that will paint a Min Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Min<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, MinPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Min(Color foreColor) => new PatternBrush(foreColor, Color.Transparent, MinPattern);
/// <summary>
/// Create as brush that will paint a Min Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Min<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, MinPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Min(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, MinPattern);
/// <summary>
/// Create as brush that will paint a Vertical Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Vertical<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, VerticalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Vertical(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, VerticalPattern);
/// <summary>
/// Create as brush that will paint a Vertical Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> Vertical<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, VerticalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush Vertical(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, VerticalPattern);
/// <summary>
/// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> ForwardDiagonal<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, ForwardDiagonalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush ForwardDiagonal(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, ForwardDiagonalPattern);
/// <summary>
/// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> ForwardDiagonal<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, ForwardDiagonalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush ForwardDiagonal(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, ForwardDiagonalPattern);
/// <summary>
/// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified foreground color and a
/// transparent background.
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> BackwardDiagonal<TPixel>(TPixel foreColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, NamedColors<TPixel>.Transparent, BackwardDiagonalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush BackwardDiagonal(Color foreColor) =>
new PatternBrush(foreColor, Color.Transparent, BackwardDiagonalPattern);
/// <summary>
/// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified colors
/// </summary>
/// <param name="foreColor">Color of the foreground.</param>
/// <param name="backColor">Color of the background.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>A New <see cref="PatternBrush{TPixel}"/></returns>
public static PatternBrush<TPixel> BackwardDiagonal<TPixel>(TPixel foreColor, TPixel backColor)
where TPixel : struct, IPixel<TPixel>
=> new PatternBrush<TPixel>(foreColor, backColor, BackwardDiagonalPattern);
/// <returns>A New <see cref="PatternBrush"/></returns>
public static PatternBrush BackwardDiagonal(Color foreColor, Color backColor) =>
new PatternBrush(foreColor, backColor, BackwardDiagonalPattern);
}
}

10
src/ImageSharp.Drawing/Processing/ColorStop{TPixel}.cs → src/ImageSharp.Drawing/Processing/ColorStop.cs

@ -10,17 +10,15 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// A struct that defines a single color stop.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[DebuggerDisplay("ColorStop({Ratio} -> {Color}")]
public struct ColorStop<TPixel>
where TPixel : struct, IPixel<TPixel>
public readonly struct ColorStop
{
/// <summary>
/// Initializes a new instance of the <see cref="ColorStop{TPixel}" /> struct.
/// Initializes a new instance of the <see cref="ColorStop" /> struct.
/// </summary>
/// <param name="ratio">Where should it be? 0 is at the start, 1 at the end of the Gradient.</param>
/// <param name="color">What color should be used at that point?</param>
public ColorStop(float ratio, TPixel color)
public ColorStop(float ratio, in Color color)
{
this.Ratio = ratio;
this.Color = color;
@ -34,6 +32,6 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Gets the color to be used.
/// </summary>
public TPixel Color { get; }
public Color Color { get; }
}
}

137
src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs

@ -1,137 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, GraphicsOptions options)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, GraphicsOptions options)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
}
}

22
src/ImageSharp.Drawing/Processing/DrawingHelpers.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.Processing
{
internal static class DrawingHelpers
{
/// <summary>
/// Convert a <see cref="DenseMatrix{Color}"/> to a <see cref="DenseMatrix{T}"/> of the given pixel type.
/// </summary>
public static DenseMatrix<TPixel> ToPixelMatrix<TPixel>(this DenseMatrix<Color> colorMatrix, Configuration configuration)
where TPixel : struct, IPixel<TPixel>
{
DenseMatrix<TPixel> result = new DenseMatrix<TPixel>(colorMatrix.Columns, colorMatrix.Rows);
Color.ToPixel(configuration, colorMatrix.Span, result.Span);
return result;
}
}
}

21
src/ImageSharp.Drawing/Processing/EllipticGradientBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs

@ -14,9 +14,7 @@ namespace SixLabors.ImageSharp.Processing
/// a point on the longest extension of the ellipse and
/// the ratio between longest and shortest extension.
/// </summary>
/// <typeparam name="TPixel">The Pixel format that is used.</typeparam>
public sealed class EllipticGradientBrush<TPixel> : GradientBrushBase<TPixel>
where TPixel : struct, IPixel<TPixel>
public sealed class EllipticGradientBrush : GradientBrushBase
{
private readonly PointF center;
@ -24,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing
private readonly float axisRatio;
/// <inheritdoc cref="GradientBrushBase{TPixel}" />
/// <inheritdoc cref="GradientBrushBase" />
/// <param name="center">The center of the elliptical gradient and 0 for the color stops.</param>
/// <param name="referenceAxisEnd">The end point of the reference axis of the ellipse.</param>
/// <param name="axisRatio">
@ -39,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing
PointF referenceAxisEnd,
float axisRatio,
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
{
this.center = center;
@ -47,12 +45,12 @@ namespace SixLabors.ImageSharp.Processing
this.axisRatio = axisRatio;
}
/// <inheritdoc cref="CreateApplicator" />
public override BrushApplicator<TPixel> CreateApplicator(
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator(
new RadialGradientBrushApplicator<TPixel>(
source,
options,
this.center,
@ -62,7 +60,8 @@ namespace SixLabors.ImageSharp.Processing
this.RepetitionMode);
/// <inheritdoc />
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
private sealed class RadialGradientBrushApplicator<TPixel> : GradientBrushApplicatorBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PointF center;
@ -85,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing
private readonly float referenceRadiusSquared;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
/// 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>
@ -102,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing
PointF center,
PointF referenceAxisEnd,
float axisRatio,
ColorStop<TPixel>[] colorStops,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
{

61
src/ImageSharp.Drawing/Processing/DrawBezierExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs

@ -15,80 +15,93 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawBeziers<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new Pen<TPixel>(brush, thickness), new Path(new CubicBezierLineSegment(points)));
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
params PointF[] points) =>
source.Draw(options, new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points)));
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawBeziers<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new Pen<TPixel>(brush, thickness), new Path(new CubicBezierLineSegment(points)));
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
IBrush brush,
float thickness,
params PointF[] points) =>
source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points)));
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawBeziers<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points);
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
Color color,
float thickness,
params PointF[] points) =>
source.DrawBeziers(new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided points as an open Bezier path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawBeziers<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.DrawBeziers(options, new SolidBrush<TPixel>(color), thickness, points);
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
params PointF[] points) =>
source.DrawBeziers(options, new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided points as an open Bezier path with the supplied pen
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawBeziers<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, pen, new Path(new CubicBezierLineSegment(points)));
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
params PointF[] points) =>
source.Draw(options, pen, new Path(new CubicBezierLineSegment(points)));
/// <summary>
/// Draws the provided points as an open Bezier path with the supplied pen
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawBeziers<TPixel>(this IImageProcessingContext<TPixel> source, IPen<TPixel> pen, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(pen, new Path(new CubicBezierLineSegment(points)));
public static IImageProcessingContext DrawBeziers(
this IImageProcessingContext source,
IPen pen,
params PointF[] points) =>
source.Draw(pen, new Path(new CubicBezierLineSegment(points)));
}
}

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

@ -0,0 +1,175 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
GraphicsOptions.Default.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
PixelColorBlendingMode colorBlending,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
colorBlending,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
GraphicsOptions options) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
Point.Empty,
options.ColorBlendingMode,
options.AlphaCompositionMode,
options.BlendPercentage));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
GraphicsOptions.Default.ColorBlendingMode,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
PixelColorBlendingMode colorBlending,
float opacity) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
colorBlending,
GraphicsOptions.Default.AlphaCompositionMode,
opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
PixelColorBlendingMode colorBlending,
PixelAlphaCompositionMode alphaComposition,
float opacity) =>
source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext DrawImage(
this IImageProcessingContext source,
Image image,
Point location,
GraphicsOptions options) =>
source.ApplyProcessor(
new DrawImageProcessor(
image,
location,
options.ColorBlendingMode,
options.AlphaCompositionMode,
options.BlendPercentage));
}
}

61
src/ImageSharp.Drawing/Processing/DrawLineExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs

@ -15,80 +15,93 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawLines<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new Pen<TPixel>(brush, thickness), new Path(new LinearLineSegment(points)));
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
params PointF[] points) =>
source.Draw(options, new Pen(brush, thickness), new Path(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawLines<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new Pen<TPixel>(brush, thickness), new Path(new LinearLineSegment(points)));
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
IBrush brush,
float thickness,
params PointF[] points) =>
source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawLines<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.DrawLines(new SolidBrush<TPixel>(color), thickness, points);
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
Color color,
float thickness,
params PointF[] points) =>
source.DrawLines(new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>>
public static IImageProcessingContext<TPixel> DrawLines<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.DrawLines(options, new SolidBrush<TPixel>(color), thickness, points);
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
params PointF[] points) =>
source.DrawLines(options, new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as an open Linear path with the supplied pen
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawLines<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, pen, new Path(new LinearLineSegment(points)));
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
params PointF[] points) =>
source.Draw(options, pen, new Path(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as an open Linear path with the supplied pen
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawLines<TPixel>(this IImageProcessingContext<TPixel> source, IPen<TPixel> pen, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(pen, new Path(new LinearLineSegment(points)));
public static IImageProcessingContext DrawLines(
this IImageProcessingContext source,
IPen pen,
params PointF[] points) =>
source.Draw(pen, new Path(new LinearLineSegment(points)));
}
}

57
src/ImageSharp.Drawing/Processing/DrawPathCollectionExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs

@ -14,14 +14,16 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
IPathCollection paths)
{
foreach (IPath path in paths)
{
@ -34,67 +36,76 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, IPen<TPixel> pen, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(GraphicsOptions.Default, pen, paths);
public static IImageProcessingContext
Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) =>
source.Draw(GraphicsOptions.Default, pen, paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The shapes.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, float thickness, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new Pen<TPixel>(brush, thickness), paths);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
IPathCollection paths) =>
source.Draw(options, new Pen(brush, thickness), paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, float thickness, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new Pen<TPixel>(brush, thickness), paths);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
IBrush brush,
float thickness,
IPathCollection paths) =>
source.Draw(new Pen(brush, thickness), paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, float thickness, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new SolidBrush<TPixel>(color), thickness, paths);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
IPathCollection paths) =>
source.Draw(options, new SolidBrush(color), thickness, paths);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, float thickness, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new SolidBrush<TPixel>(color), thickness, paths);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
Color color,
float thickness,
IPathCollection paths) =>
source.Draw(new SolidBrush(color), thickness, paths);
}
}

58
src/ImageSharp.Drawing/Processing/DrawPathExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs

@ -15,80 +15,90 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, pen.StrokeFill, new ShapePath(path, pen));
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
IPath path) =>
source.Fill(options, pen.StrokeFill, new ShapePath(path, pen));
/// <summary>
/// Draws the outline of the polygon with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, IPen<TPixel> pen, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(GraphicsOptions.Default, pen, path);
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) =>
source.Draw(GraphicsOptions.Default, pen, path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, float thickness, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new Pen<TPixel>(brush, thickness), path);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
IPath path) =>
source.Draw(options, new Pen(brush, thickness), path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, float thickness, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new Pen<TPixel>(brush, thickness), path);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
IBrush brush,
float thickness,
IPath path) =>
source.Draw(new Pen(brush, thickness), path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, float thickness, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new SolidBrush<TPixel>(color), thickness, path);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
IPath path) =>
source.Draw(options, new SolidBrush(color), thickness, path);
/// <summary>
/// Draws the outline of the polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, float thickness, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new SolidBrush<TPixel>(color), thickness, path);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
Color color,
float thickness,
IPath path) =>
source.Draw(new SolidBrush(color), thickness, path);
}
}

61
src/ImageSharp.Drawing/Processing/DrawPolygonExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs

@ -15,80 +15,93 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawPolygon<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new Pen<TPixel>(brush, thickness), new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
params PointF[] points) =>
source.Draw(options, new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawPolygon<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new Pen<TPixel>(brush, thickness), new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
IBrush brush,
float thickness,
params PointF[] points) =>
source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawPolygon<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points);
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
Color color,
float thickness,
params PointF[] points) =>
source.DrawPolygon(new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawPolygon<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, float thickness, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.DrawPolygon(options, new SolidBrush<TPixel>(color), thickness, points);
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
params PointF[] points) =>
source.DrawPolygon(options, new SolidBrush(color), thickness, points);
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided Pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawPolygon<TPixel>(this IImageProcessingContext<TPixel> source, IPen<TPixel> pen, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
IPen pen,
params PointF[] points) =>
source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Draws the provided Points as a closed Linear Polygon with the provided Pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawPolygon<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, pen, new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext DrawPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
params PointF[] points) =>
source.Draw(options, pen, new Polygon(new LinearLineSegment(points)));
}
}

58
src/ImageSharp.Drawing/Processing/DrawRectangleExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs

@ -15,80 +15,90 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the outline of the rectangle with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="pen">The pen.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, pen, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IPen pen,
RectangleF shape) =>
source.Draw(options, pen, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Draws the outline of the rectangle with the provided pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="pen">The pen.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, IPen<TPixel> pen, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(GraphicsOptions.Default, pen, shape);
public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) =>
source.Draw(GraphicsOptions.Default, pen, shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, float thickness, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new Pen<TPixel>(brush, thickness), shape);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
float thickness,
RectangleF shape) =>
source.Draw(options, new Pen(brush, thickness), shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, float thickness, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new Pen<TPixel>(brush, thickness), shape);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
IBrush brush,
float thickness,
RectangleF shape) =>
source.Draw(new Pen(brush, thickness), shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, float thickness, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, new SolidBrush<TPixel>(color), thickness, shape);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
float thickness,
RectangleF shape) =>
source.Draw(options, new SolidBrush(color), thickness, shape);
/// <summary>
/// Draws the outline of the rectangle with the provided brush at the provided thickness.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, float thickness, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(new SolidBrush<TPixel>(color), thickness, shape);
public static IImageProcessingContext Draw(
this IImageProcessingContext source,
Color color,
float thickness,
RectangleF shape) =>
source.Draw(new SolidBrush(color), thickness, shape);
}
}

94
src/ImageSharp.Drawing/Processing/DrawTextExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs

@ -16,7 +16,6 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
@ -25,14 +24,17 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, TPixel color, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, color, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
Color color,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, color, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
@ -42,14 +44,18 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, TPixel color, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(options, text, font, Brushes.Solid(color), null, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
TextGraphicsOptions options,
string text,
Font font,
Color color,
PointF location) =>
source.DrawText(options, text, font, Brushes.Solid(color), null, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
@ -58,14 +64,17 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, IBrush<TPixel> brush, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, brush, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
IBrush brush,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, brush, location);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
@ -75,14 +84,18 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(options, text, font, brush, null, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
TextGraphicsOptions options,
string text,
Font font,
IBrush brush,
PointF location) =>
source.DrawText(options, text, font, brush, null, location);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
@ -91,14 +104,17 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, pen, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
IPen pen,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, text, font, pen, location);
/// <summary>
/// Draws the text onto the the image outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
@ -108,14 +124,18 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(options, text, font, null, pen, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
TextGraphicsOptions options,
string text,
Font font,
IPen pen,
PointF location) =>
source.DrawText(options, text, font, null, pen, location);
/// <summary>
/// Draws the text onto the the image filled via the brush then outlined via the pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
@ -125,14 +145,18 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location);
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
string text,
Font font,
IBrush brush,
IPen pen,
PointF location) =>
source.DrawText(TextGraphicsOptions.Default, 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.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="text">The text.</param>
@ -143,8 +167,14 @@ namespace SixLabors.ImageSharp.Processing
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static IImageProcessingContext<TPixel> DrawText<TPixel>(this IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawTextProcessor<TPixel>(options, text, font, brush, pen, location));
public static IImageProcessingContext DrawText(
this IImageProcessingContext source,
TextGraphicsOptions options,
string text,
Font font,
IBrush brush,
IPen pen,
PointF location) =>
source.ApplyProcessor(new DrawTextProcessor(options, text, font, brush, pen, location));
}
}

36
src/ImageSharp.Drawing/Processing/FillPathBuilderExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs

@ -15,14 +15,16 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, Action<PathBuilder> path)
where TPixel : struct, IPixel<TPixel>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
Action<PathBuilder> path)
{
var pb = new PathBuilder();
path(pb);
@ -33,38 +35,42 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, Action<PathBuilder> path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(GraphicsOptions.Default, brush, path);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
IBrush brush,
Action<PathBuilder> path) =>
source.Fill(GraphicsOptions.Default, brush, path);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, Action<PathBuilder> path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, new SolidBrush<TPixel>(color), path);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
Action<PathBuilder> path) =>
source.Fill(options, new SolidBrush(color), path);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, Action<PathBuilder> path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color), path);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
Color color,
Action<PathBuilder> path) =>
source.Fill(new SolidBrush(color), path);
}
}

36
src/ImageSharp.Drawing/Processing/FillPathCollectionExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs

@ -14,14 +14,16 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="paths">The shapes.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
IPathCollection paths)
{
foreach (IPath s in paths)
{
@ -34,38 +36,42 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(GraphicsOptions.Default, brush, paths);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
IBrush brush,
IPathCollection paths) =>
source.Fill(GraphicsOptions.Default, brush, paths);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, new SolidBrush<TPixel>(color), paths);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
IPathCollection paths) =>
source.Fill(options, new SolidBrush(color), paths);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="paths">The paths.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, IPathCollection paths)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color), paths);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
Color color,
IPathCollection paths) =>
source.Fill(new SolidBrush(color), paths);
}
}

33
src/ImageSharp.Drawing/Processing/FillPathExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Shapes;
@ -15,51 +14,51 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, brush, new ShapeRegion(path));
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
IPath path) =>
source.Fill(options, brush, new ShapeRegion(path));
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path));
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) =>
source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path));
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, new SolidBrush<TPixel>(color), path);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
IPath path) =>
source.Fill(options, new SolidBrush(color), path);
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, IPath path)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color), path);
public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, IPath path) =>
source.Fill(new SolidBrush(color), path);
}
}

38
src/ImageSharp.Drawing/Processing/FillPolygonExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs

@ -15,51 +15,57 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> FillPolygon<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, brush, new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
params PointF[] points) =>
source.Fill(options, brush, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> FillPolygon<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(brush, new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
IBrush brush,
params PointF[] points) =>
source.Fill(brush, new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> FillPolygon<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, new SolidBrush<TPixel>(color), new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
params PointF[] points) =>
source.Fill(options, new SolidBrush(color), new Polygon(new LinearLineSegment(points)));
/// <summary>
/// Flood fills the image in the shape of a Linear polygon described by the points
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> FillPolygon<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, params PointF[] points)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color), new Polygon(new LinearLineSegment(points)));
public static IImageProcessingContext FillPolygon(
this IImageProcessingContext source,
Color color,
params PointF[] points) =>
source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)));
}
}

34
src/ImageSharp.Drawing/Processing/FillRectangleExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs

@ -15,51 +15,53 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="brush">The brush.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
RectangleF shape) =>
source.Fill(options, brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
public static IImageProcessingContext
Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) =>
source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, new SolidBrush<TPixel>(color), shape);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
RectangleF shape) =>
source.Fill(options, new SolidBrush(color), shape);
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color), shape);
public static IImageProcessingContext
Fill(this IImageProcessingContext source, Color color, RectangleF shape) =>
source.Fill(new SolidBrush(color), shape);
}
}

54
src/ImageSharp.Drawing/Processing/FillRegionExtensions.cs → src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs

@ -1,7 +1,6 @@
// 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.Processors.Drawing;
@ -15,85 +14,82 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Flood fills the image with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The details how to fill the region of interest.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(GraphicsOptions.Default, brush);
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) =>
source.Fill(GraphicsOptions.Default, brush);
/// <summary>
/// Flood fills the image with the specified color.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color));
public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color) =>
source.Fill(new SolidBrush(color));
/// <summary>
/// Flood fills the image with in the region with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, Region region)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(GraphicsOptions.Default, brush, region);
public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) =>
source.Fill(GraphicsOptions.Default, brush, region);
/// <summary>
/// Flood fills the image with in the region with the specified color.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The options.</param>
/// <param name="color">The color.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, TPixel color, Region region)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, new SolidBrush<TPixel>(color), region);
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
Color color,
Region region) =>
source.Fill(options, new SolidBrush(color), region);
/// <summary>
/// Flood fills the image with in the region with the specified color.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, TPixel color, Region region)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(new SolidBrush<TPixel>(color), region);
public static IImageProcessingContext Fill(this IImageProcessingContext source, Color color, Region region) =>
source.Fill(new SolidBrush(color), region);
/// <summary>
/// Flood fills the image with in the region with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The brush.</param>
/// <param name="region">The region.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, Region region)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new FillRegionProcessor<TPixel>(brush, region, options));
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush,
Region region) =>
source.ApplyProcessor(new FillRegionProcessor(brush, region, options));
/// <summary>
/// Flood fills the image with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="options">The graphics options.</param>
/// <param name="brush">The details how to fill the region of interest.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new FillProcessor<TPixel>(brush, options));
public static IImageProcessingContext Fill(
this IImageProcessingContext source,
GraphicsOptions options,
IBrush brush) =>
source.ApplyProcessor(new FillProcessor(brush, options));
}
}

42
src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs → src/ImageSharp.Drawing/Processing/GradientBrushBase.cs

@ -13,16 +13,14 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Base class for Gradient brushes
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
public abstract class GradientBrushBase<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
public abstract class GradientBrushBase : IBrush
{
/// <inheritdoc cref="IBrush{TPixel}"/>
/// <inheritdoc cref="IBrush"/>
/// <param name="repetitionMode">Defines how the colors are repeated beyond the interval [0..1]</param>
/// <param name="colorStops">The gradient colors.</param>
protected GradientBrushBase(
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
params ColorStop[] colorStops)
{
this.RepetitionMode = repetitionMode;
this.ColorStops = colorStops;
@ -36,25 +34,29 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Gets the list of color stops for this gradient.
/// </summary>
protected ColorStop<TPixel>[] ColorStops { get; }
protected ColorStop[] ColorStops { get; }
/// <inheritdoc cref="IBrush{TPixel}" />
public abstract BrushApplicator<TPixel> CreateApplicator(
/// <inheritdoc />
public abstract BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options);
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Base class for gradient brush applicators
/// </summary>
protected abstract class GradientBrushApplicatorBase : BrushApplicator<TPixel>
internal abstract class GradientBrushApplicatorBase<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly ColorStop<TPixel>[] colorStops;
private static readonly TPixel Transparent = Color.Transparent.ToPixel<TPixel>();
private readonly ColorStop[] colorStops;
private readonly GradientRepetitionMode repetitionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GradientBrushApplicatorBase"/> class.
/// Initializes a new instance of the <see cref="GradientBrushApplicatorBase{TPixel}"/> class.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="options">The options.</param>
@ -63,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing
protected GradientBrushApplicatorBase(
ImageFrame<TPixel> target,
GraphicsOptions options,
ColorStop<TPixel>[] colorStops,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options)
{
@ -103,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing
case GradientRepetitionMode.DontFill:
if (positionOnCompleteGradient > 1 || positionOnCompleteGradient < 0)
{
return NamedColors<TPixel>.Transparent;
return Transparent;
}
break;
@ -111,11 +113,11 @@ namespace SixLabors.ImageSharp.Processing
throw new ArgumentOutOfRangeException();
}
(ColorStop<TPixel> from, ColorStop<TPixel> to) = this.GetGradientSegment(positionOnCompleteGradient);
(ColorStop from, ColorStop to) = this.GetGradientSegment(positionOnCompleteGradient);
if (from.Color.Equals(to.Color))
{
return from.Color;
return from.Color.ToPixel<TPixel>();
}
else
{
@ -150,14 +152,14 @@ namespace SixLabors.ImageSharp.Processing
/// </returns>
protected abstract float PositionOnGradient(float x, float y);
private (ColorStop<TPixel> from, ColorStop<TPixel> to) GetGradientSegment(
private (ColorStop from, ColorStop to) GetGradientSegment(
float positionOnCompleteGradient)
{
ColorStop<TPixel> localGradientFrom = this.colorStops[0];
ColorStop<TPixel> localGradientTo = default;
ColorStop localGradientFrom = this.colorStops[0];
ColorStop localGradientTo = default;
// TODO: ensure colorStops has at least 2 items (technically 1 would be okay, but that's no gradient)
foreach (ColorStop<TPixel> colorStop in this.colorStops)
foreach (ColorStop colorStop in this.colorStops)
{
localGradientTo = colorStop;

6
src/ImageSharp.Drawing/Processing/GradientRepetitionMode.cs

@ -21,16 +21,16 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Reflect the gradient.
/// Similar to <see cref="Repeat"/>, but each other repetition uses inverse order of <see cref="ColorStop{TPixel}"/>s.
/// Similar to <see cref="Repeat"/>, but each other repetition uses inverse order of <see cref="ColorStop"/>s.
/// Used on a Black-White gradient, Reflect leads to Black->{gray}->White->{gray}->White...
/// </summary>
Reflect,
/// <summary>
/// With DontFill a gradient does not touch any pixel beyond it's borders.
/// For the <see cref="LinearGradientBrush{TPixel}" /> this is beyond the orthogonal through start and end,
/// For the <see cref="LinearGradientBrush" /> this is beyond the orthogonal through start and end,
/// TODO For the cref="PolygonalGradientBrush" it's outside the polygon,
/// For <see cref="RadialGradientBrush{TPixel}" /> and <see cref="EllipticGradientBrush{TPixel}" /> it's beyond 1.0.
/// For <see cref="RadialGradientBrush" /> and <see cref="EllipticGradientBrush" /> it's beyond 1.0.
/// </summary>
DontFill
}

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

@ -9,17 +9,16 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Brush represents a logical configuration of a brush which can be used to source pixel colors
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <remarks>
/// A brush is a simple class that will return an <see cref="BrushApplicator{TPixel}" /> that will perform the
/// logic for converting a pixel location to a <typeparamref name="TPixel"/>.
/// logic for retrieving pixel values for specific locations.
/// </remarks>
public interface IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
public interface IBrush
{
/// <summary>
/// Creates the applicator for this brush.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <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>
@ -30,6 +29,10 @@ namespace SixLabors.ImageSharp.Processing
/// The <paramref name="region" /> when being applied to things like shapes would usually be the
/// bounding box of the shape not necessarily the bounds of the whole image
/// </remarks>
BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options);
BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>;
}
}

17
src/ImageSharp.Drawing/Processing/IPen.cs

@ -2,28 +2,21 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Interface representing a Pen
/// Interface representing the pattern and size of the stroke to apply with a Pen.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
public interface IPen<TPixel> : IPen
where TPixel : struct, IPixel<TPixel>
public interface IPen
{
/// <summary>
/// Gets the stroke fill.
/// </summary>
IBrush<TPixel> StrokeFill { get; }
}
IBrush StrokeFill { get; }
/// <summary>
/// Interface representing the pattern and size of the stroke to apply with a Pen.
/// </summary>
public interface IPen
{
/// <summary>
/// Gets the width to apply to the stroke
/// </summary>
@ -34,4 +27,4 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
ReadOnlySpan<float> StrokePattern { get; }
}
}
}

73
src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/ImageBrush.cs

@ -3,6 +3,7 @@
using System;
using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -14,46 +15,50 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Provides an implementation of an image brush for painting images within areas.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class ImageBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
public class ImageBrush : IBrush
{
/// <summary>
/// The image to paint.
/// </summary>
private readonly ImageFrame<TPixel> image;
private readonly Image image;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class.
/// Initializes a new instance of the <see cref="ImageBrush"/> class.
/// </summary>
/// <param name="image">The image.</param>
public ImageBrush(ImageFrame<TPixel> image)
public ImageBrush(Image image)
{
this.image = image;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class.
/// </summary>
/// <param name="image">The image.</param>
public ImageBrush(Image<TPixel> image)
: this(image.Frames.RootFrame)
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
}
if (this.image is Image<TPixel> specificImage)
{
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, false);
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
=> new ImageBrushApplicator(source, this.image, region, options);
specificImage = this.image.CloneAs<TPixel>();
return new ImageBrushApplicator<TPixel>(source, specificImage, region, options, true);
}
/// <summary>
/// The image brush applicator.
/// </summary>
private class ImageBrushApplicator : BrushApplicator<TPixel>
private class ImageBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The source image.
/// </summary>
private readonly ImageFrame<TPixel> source;
private ImageFrame<TPixel> sourceFrame;
private Image<TPixel> sourceImage;
private readonly bool shouldDisposeImage;
/// <summary>
/// The y-length.
@ -76,16 +81,24 @@ namespace SixLabors.ImageSharp.Processing
private readonly int offsetX;
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrushApplicator"/> class.
/// Initializes a new instance of the <see cref="ImageBrushApplicator{TPixel}"/> class.
/// </summary>
/// <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>
public ImageBrushApplicator(ImageFrame<TPixel> target, ImageFrame<TPixel> image, RectangleF region, GraphicsOptions options)
/// <param name="shouldDisposeImage">Whether to dispose the image on disposal of the applicator.</param>
public ImageBrushApplicator(
ImageFrame<TPixel> target,
Image<TPixel> image,
RectangleF region,
GraphicsOptions options,
bool shouldDisposeImage)
: base(target, options)
{
this.source = image;
this.sourceImage = image;
this.sourceFrame = image.Frames.RootFrame;
this.shouldDisposeImage = shouldDisposeImage;
this.xLength = image.Width;
this.yLength = image.Height;
this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0);
@ -106,13 +119,19 @@ namespace SixLabors.ImageSharp.Processing
{
int srcX = (x - this.offsetX) % this.xLength;
int srcY = (y - this.offsetY) % this.yLength;
return this.source[srcX, srcY];
return this.sourceFrame[srcX, srcY];
}
}
/// <inheritdoc />
public override void Dispose()
{
if (this.shouldDisposeImage)
{
this.sourceImage?.Dispose();
this.sourceImage = null;
this.sourceFrame = null;
}
}
/// <inheritdoc />
@ -127,7 +146,7 @@ namespace SixLabors.ImageSharp.Processing
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(sourceY);
Span<TPixel> sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY);
for (int i = 0; i < scanline.Length; i++)
{
@ -140,7 +159,7 @@ namespace SixLabors.ImageSharp.Processing
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
this.Blender.Blend(
this.source.Configuration,
this.sourceFrame.Configuration,
destinationRow,
destinationRow,
overlaySpan,

32
src/ImageSharp.Drawing/Processing/LinearGradientBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs

@ -13,16 +13,14 @@ namespace SixLabors.ImageSharp.Processing
/// Supported right now:
/// - a set of colors in relative distances to each other.
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
public sealed class LinearGradientBrush<TPixel> : GradientBrushBase<TPixel>
where TPixel : struct, IPixel<TPixel>
public sealed class LinearGradientBrush : GradientBrushBase
{
private readonly PointF p1;
private readonly PointF p2;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrush{TPixel}"/> class.
/// Initializes a new instance of the <see cref="LinearGradientBrush"/> class.
/// </summary>
/// <param name="p1">Start point</param>
/// <param name="p2">End point</param>
@ -32,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing
PointF p1,
PointF p2,
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
{
this.p1 = p1;
@ -40,13 +38,23 @@ namespace SixLabors.ImageSharp.Processing
}
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
=> new LinearGradientBrushApplicator(source, this.p1, this.p2, this.ColorStops, this.RepetitionMode, options);
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new LinearGradientBrushApplicator<TPixel>(
source,
this.p1,
this.p2,
this.ColorStops,
this.RepetitionMode,
options);
/// <summary>
/// The linear gradient brush applicator.
/// </summary>
private sealed class LinearGradientBrushApplicator : GradientBrushApplicatorBase
private sealed class LinearGradientBrushApplicator<TPixel> : GradientBrushApplicatorBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PointF start;
@ -83,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing
private readonly float length;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushApplicator" /> class.
/// 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>
@ -95,7 +103,7 @@ namespace SixLabors.ImageSharp.Processing
ImageFrame<TPixel> source,
PointF start,
PointF end,
ColorStop<TPixel>[] colorStops,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode,
GraphicsOptions options)
: base(source, options, colorStops, repetitionMode)
@ -137,9 +145,7 @@ namespace SixLabors.ImageSharp.Processing
float y4 = y + (k * this.alongX);
// get distance from (x4,y4) to start
float distance = (float)Math.Sqrt(
Math.Pow(x4 - this.start.X, 2)
+ Math.Pow(y4 - this.start.Y, 2));
float distance = (float)Math.Sqrt(Math.Pow(x4 - this.start.X, 2) + Math.Pow(y4 - this.start.Y, 2));
// get and return ratio
float ratio = distance / this.length;

36
src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/PatternBrush.cs

@ -34,38 +34,36 @@ namespace SixLabors.ImageSharp.Processing
/// 0
/// </para>
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class PatternBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
public class PatternBrush : IBrush
{
/// <summary>
/// The pattern.
/// </summary>
private readonly DenseMatrix<TPixel> pattern;
private readonly DenseMatrix<Color> pattern;
private readonly DenseMatrix<Vector4> patternVector;
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush{TPixel}"/> class.
/// Initializes a new instance of the <see cref="PatternBrush"/> class.
/// </summary>
/// <param name="foreColor">Color of the fore.</param>
/// <param name="backColor">Color of the back.</param>
/// <param name="pattern">The pattern.</param>
public PatternBrush(TPixel foreColor, TPixel backColor, bool[,] pattern)
public PatternBrush(Color foreColor, Color backColor, bool[,] pattern)
: this(foreColor, backColor, new DenseMatrix<bool>(pattern))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush{TPixel}"/> class.
/// Initializes a new instance of the <see cref="PatternBrush"/> class.
/// </summary>
/// <param name="foreColor">Color of the fore.</param>
/// <param name="backColor">Color of the back.</param>
/// <param name="pattern">The pattern.</param>
internal PatternBrush(TPixel foreColor, TPixel backColor, in DenseMatrix<bool> pattern)
internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix<bool> pattern)
{
var foreColorVector = foreColor.ToVector4();
var backColorVector = backColor.ToVector4();
this.pattern = new DenseMatrix<TPixel>(pattern.Columns, pattern.Rows);
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++)
{
@ -83,22 +81,32 @@ namespace SixLabors.ImageSharp.Processing
}
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush{TPixel}"/> class.
/// Initializes a new instance of the <see cref="PatternBrush"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
internal PatternBrush(PatternBrush<TPixel> brush)
internal PatternBrush(PatternBrush brush)
{
this.pattern = brush.pattern;
this.patternVector = brush.patternVector;
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options) => new PatternBrushApplicator(source, this.pattern, this.patternVector, options);
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> =>
new PatternBrushApplicator<TPixel>(
source,
this.pattern.ToPixelMatrix<TPixel>(source.Configuration),
this.patternVector,
options);
/// <summary>
/// The pattern brush applicator.
/// </summary>
private class PatternBrushApplicator : BrushApplicator<TPixel>
private class PatternBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The pattern.
@ -107,7 +115,7 @@ namespace SixLabors.ImageSharp.Processing
private readonly DenseMatrix<Vector4> patternVector;
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator" /> class.
/// Initializes a new instance of the <see cref="PatternBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="pattern">The pattern.</param>

26
src/ImageSharp.Drawing/Processing/Pen{TPixel}.cs → src/ImageSharp.Drawing/Processing/Pen.cs

@ -9,7 +9,6 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Provides a pen that can apply a pattern to a line with a set brush and thickness
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <remarks>
/// The pattern will be in to the form of new float[]{ 1f, 2f, 0.5f} this will be
/// converted into a pattern that is 3.5 times longer that the width with 3 sections
@ -18,29 +17,28 @@ namespace SixLabors.ImageSharp.Processing
/// section 3 will be width/2 long and will be filled
/// the the pattern will immediately repeat without gap.
/// </remarks>
public class Pen<TPixel> : IPen<TPixel>
where TPixel : struct, IPixel<TPixel>
public class Pen : IPen
{
private readonly float[] pattern;
/// <summary>
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <param name="pattern">The pattern.</param>
public Pen(TPixel color, float width, float[] pattern)
: this(new SolidBrush<TPixel>(color), width, pattern)
public Pen(Color color, float width, float[] pattern)
: this(new SolidBrush(color), width, pattern)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <param name="pattern">The pattern.</param>
public Pen(IBrush<TPixel> brush, float width, float[] pattern)
public Pen(IBrush brush, float width, float[] pattern)
{
this.StrokeFill = brush;
this.StrokeWidth = width;
@ -48,27 +46,27 @@ namespace SixLabors.ImageSharp.Processing
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
public Pen(TPixel color, float width)
: this(new SolidBrush<TPixel>(color), width)
public Pen(Color color, float width)
: this(new SolidBrush(color), width)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen{TPixel}"/> class.
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
public Pen(IBrush<TPixel> brush, float width)
public Pen(IBrush brush, float width)
: this(brush, width, Pens.EmptyPattern)
{
}
/// <inheritdoc/>
public IBrush<TPixel> StrokeFill { get; }
public IBrush StrokeFill { get; }
/// <inheritdoc/>
public float StrokeWidth { get; }

50
src/ImageSharp.Drawing/Processing/Pens.cs

@ -21,109 +21,79 @@ namespace SixLabors.ImageSharp.Processing
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> Solid<TPixel>(TPixel color, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(color, width);
public static Pen Solid(Color color, float width) => new Pen(color, width);
/// <summary>
/// Create a solid pen with out any drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> Solid<TPixel>(IBrush<TPixel> brush, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(brush, width);
public static Pen Solid(IBrush brush, float width) => new Pen(brush, width);
/// <summary>
/// Create a pen with a 'Dash' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> Dash<TPixel>(TPixel color, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(color, width, DashedPattern);
public static Pen Dash(Color color, float width) => new Pen(color, width, DashedPattern);
/// <summary>
/// Create a pen with a 'Dash' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> Dash<TPixel>(IBrush<TPixel> brush, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(brush, width, DashedPattern);
public static Pen Dash(IBrush brush, float width) => new Pen(brush, width, DashedPattern);
/// <summary>
/// Create a pen with a 'Dot' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> Dot<TPixel>(TPixel color, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(color, width, DottedPattern);
public static Pen Dot(Color color, float width) => new Pen(color, width, DottedPattern);
/// <summary>
/// Create a pen with a 'Dot' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> Dot<TPixel>(IBrush<TPixel> brush, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(brush, width, DottedPattern);
public static Pen Dot(IBrush brush, float width) => new Pen(brush, width, DottedPattern);
/// <summary>
/// Create a pen with a 'Dash Dot' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> DashDot<TPixel>(TPixel color, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(color, width, DashDotPattern);
public static Pen DashDot(Color color, float width) => new Pen(color, width, DashDotPattern);
/// <summary>
/// Create a pen with a 'Dash Dot' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> DashDot<TPixel>(IBrush<TPixel> brush, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(brush, width, DashDotPattern);
public static Pen DashDot(IBrush brush, float width) => new Pen(brush, width, DashDotPattern);
/// <summary>
/// Create a pen with a 'Dash Dot Dot' drawing patterns
/// </summary>
/// <param name="color">The color.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> DashDotDot<TPixel>(TPixel color, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(color, width, DashDotDotPattern);
public static Pen DashDotDot(Color color, float width) => new Pen(color, width, DashDotDotPattern);
/// <summary>
/// Create a pen with a 'Dash Dot Dot' drawing patterns
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="width">The width.</param>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <returns>The Pen</returns>
public static Pen<TPixel> DashDotDot<TPixel>(IBrush<TPixel> brush, float width)
where TPixel : struct, IPixel<TPixel>
=> new Pen<TPixel>(brush, width, DashDotDotPattern);
public static Pen DashDotDot(IBrush brush, float width) => new Pen(brush, width, DashDotDotPattern);
}
}

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

@ -1,12 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
@ -16,90 +14,86 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
public class DrawImageProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="image">The image to blend.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
/// <param name="opacity">The opacity of the image to blend.</param>
public DrawImageProcessor(
Image image,
Point location,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image;
this.Opacity = opacity;
this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location;
this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity;
}
/// <summary>
/// Gets the image to blend
/// Gets the image to blend.
/// </summary>
public Image<TPixelSrc> Image { get; }
public Image Image { get; }
/// <summary>
/// Gets the opacity of the image to blend
/// Gets the location to draw the blended image.
/// </summary>
public float Opacity { get; }
public Point Location { get; }
/// <summary>
/// Gets the pixel blender
/// Gets the blending mode to use when drawing the image.
/// </summary>
public PixelBlender<TPixelDst> Blender { get; }
public PixelColorBlendingMode ColorBlendingMode { get; }
/// <summary>
/// Gets the location to draw the blended image
/// Gets the Alpha blending mode to use when drawing the image.
/// </summary>
public Point Location { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
{
Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y;
public PixelAlphaCompositionMode AlphaCompositionMode { get; }
// Align start/end positions.
Rectangle bounds = targetImage.Bounds();
/// <summary>
/// Gets the opacity of the image to blend.
/// </summary>
public float Opacity { get; }
int minX = Math.Max(this.Location.X, sourceRectangle.X);
int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right);
int targetX = minX - this.Location.X;
/// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>()
where TPixelBg : struct, IPixel<TPixelBg>
{
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
private class ProcessorFactoryVisitor<TPixelBg> : IImageVisitor
where TPixelBg : struct, IPixel<TPixelBg>
{
private readonly DrawImageProcessor definition;
int width = maxX - minX;
public ProcessorFactoryVisitor(DrawImageProcessor definition)
{
this.definition = definition;
}
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
public IImageProcessor<TPixelBg> Result { get; private set; }
// not a valid operation because rectangle does not overlap with this image.
if (workingRect.Width <= 0 || workingRect.Height <= 0)
public void Visit<TPixelFg>(Image<TPixelFg> image)
where TPixelFg : struct, IPixel<TPixelFg>
{
throw new ImageProcessingException("Cannot draw image because the source image does not overlap the target image.");
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
image,
this.definition.Location,
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
this.definition.Opacity);
}
ParallelHelper.IterateRows(
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(configuration, background, background, foreground, this.Opacity);
}
});
}
}
}

111
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

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

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

@ -1,124 +1,45 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// Defines a processor to fill an <see cref="Image"/> with the given <see cref="IBrush"/>
/// using blending defined by the given <see cref="GraphicsOptions"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
public class FillProcessor : IImageProcessor
{
/// <summary>
/// The brush.
/// Initializes a new instance of the <see cref="FillProcessor"/> class.
/// </summary>
private readonly IBrush<TPixel> brush;
private readonly GraphicsOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="FillProcessor{TPixel}"/> class.
/// </summary>
/// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="options">The options</param>
public FillProcessor(IBrush<TPixel> brush, GraphicsOptions options)
/// <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)
{
this.brush = brush;
this.options = options;
this.Brush = brush;
this.Options = options;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
int width = maxX - minX;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
// If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush))
{
ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);
ParallelHelper.IterateRows(
workingRect,
parallelSettings,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
}
});
}
else
{
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
amount.GetSpan().Fill(1f);
ParallelHelper.IterateRows(
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
int offsetY = y - startY;
int offsetX = minX - startX;
/// <summary>
/// Gets the <see cref="IBrush"/> used for filling the destination image.
/// </summary>
public IBrush Brush { get; }
applicator.Apply(amount.GetSpan(), offsetX, offsetY);
}
});
}
}
}
/// <summary>
/// Gets the <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.
/// </summary>
public GraphicsOptions Options { get; }
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
solidBrush = this.brush as SolidBrush<TPixel>;
if (solidBrush == null)
{
return false;
}
return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color);
return new FillProcessor<TPixel>(this);
}
}
}

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

@ -0,0 +1,118 @@
// 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.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class FillProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly FillProcessor definition;
public FillProcessor(FillProcessor definition)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
int width = maxX - minX;
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
IBrush brush = this.definition.Brush;
GraphicsOptions options = this.definition.Options;
// If there's no reason for blending, then avoid it.
if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush))
{
ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);
TPixel colorPixel = solidBrush.Color.ToPixel<TPixel>();
ParallelHelper.IterateRows(
workingRect,
parallelSettings,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel);
}
});
}
else
{
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = brush.CreateApplicator(
source,
sourceRectangle,
options))
{
amount.GetSpan().Fill(1f);
ParallelHelper.IterateRows(
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
int offsetY = y - startY;
int offsetX = minX - startX;
applicator.Apply(amount.GetSpan(), offsetX, offsetY);
}
});
}
}
}
private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush)
{
solidBrush = this.definition.Brush as SolidBrush;
if (solidBrush == null)
{
return false;
}
return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
}

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

@ -1,36 +1,25 @@
// 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.ImageSharp.Primitives;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using a brush and a shape fills shape with contents of brush the
/// Defines a processor to fill <see cref="Image"/> pixels withing a given <see cref="Region"/>
/// with the given <see cref="IBrush"/> and blending defined by the given <see cref="GraphicsOptions"/>.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <seealso cref="ImageProcessor{TPixel}" />
internal class FillRegionProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
public class FillRegionProcessor : IImageProcessor
{
private const float AntialiasFactor = 1f;
private const int DrawPadding = 1;
/// <summary>
/// Initializes a new instance of the <see cref="FillRegionProcessor{TPixel}" /> class.
/// Initializes a new instance of the <see cref="FillRegionProcessor" /> class.
/// </summary>
/// <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<TPixel> brush, Region region, GraphicsOptions options)
public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options)
{
this.Region = region;
this.Brush = brush;
@ -38,9 +27,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
}
/// <summary>
/// Gets the brush.
/// Gets the <see cref="IBrush"/> used for filling the destination image.
/// </summary>
public IBrush<TPixel> Brush { get; }
public IBrush Brush { get; }
/// <summary>
/// Gets the region that this processor applies to.
@ -48,172 +37,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public Region Region { get; }
/// <summary>
/// Gets the options.
/// Gets the <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.
/// </summary>
/// <value>
/// The options.
/// </value>
public GraphicsOptions Options { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
Region region = this.Region;
Rectangle rect = region.Bounds;
// Align start/end positions.
int minX = Math.Max(0, rect.Left);
int maxX = Math.Min(source.Width, rect.Right);
int minY = Math.Max(0, rect.Top);
int maxY = Math.Min(source.Height, rect.Bottom);
if (minX >= maxX)
{
return; // no effect inside image;
}
if (minY >= maxY)
{
return; // no effect inside image;
}
int maxIntersections = region.MaxIntersections;
float subpixelCount = 4;
// we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
// region to align with the pixel grid.
float offset = 0.5f;
if (this.Options.Antialias)
{
offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
subpixelCount = 4;
}
}
using (BrushApplicator<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
int scanlineWidth = maxX - minX;
using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
using (IMemoryOwner<float> bScanline = source.MemoryAllocator.Allocate<float>(scanlineWidth))
{
bool scanlineDirty = true;
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush);
for (int y = minY; y < maxY; y++)
{
if (scanlineDirty)
{
scanline.Clear();
scanlineDirty = false;
}
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
{
// nothing on this line, skip
continue;
}
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
nextX = Math.Max(nextX, 0);
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
if (scanlineDirty)
{
if (!this.Options.Antialias)
{
bool hasOnes = false;
bool hasZeros = false;
for (int x = 0; x < scanlineWidth; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
hasOnes = true;
}
else
{
scanline[x] = 0;
hasZeros = true;
}
}
if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
{
if (hasOnes)
{
source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrush.Color);
}
continue;
}
}
applicator.Apply(scanline, minX, y);
}
}
}
}
}
private bool IsSolidBrushWithoutBlending(out SolidBrush<TPixel> solidBrush)
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
solidBrush = this.Brush as SolidBrush<TPixel>;
if (solidBrush == null)
{
return false;
}
return this.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
return new FillRegionProcessor<TPixel>(this);
}
}
}

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

@ -0,0 +1,195 @@
// 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.ImageSharp.Primitives;
using SixLabors.ImageSharp.Utils;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Using a brush and a shape fills shape with contents of brush the
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <seealso cref="ImageProcessor{TPixel}" />
internal class FillRegionProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly FillRegionProcessor definition;
public FillRegionProcessor(FillRegionProcessor definition)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
GraphicsOptions options = this.definition.Options;
IBrush brush = this.definition.Brush;
Region region = this.definition.Region;
Rectangle rect = region.Bounds;
// Align start/end positions.
int minX = Math.Max(0, rect.Left);
int maxX = Math.Min(source.Width, rect.Right);
int minY = Math.Max(0, rect.Top);
int maxY = Math.Min(source.Height, rect.Bottom);
if (minX >= maxX)
{
return; // no effect inside image;
}
if (minY >= maxY)
{
return; // no effect inside image;
}
int maxIntersections = region.MaxIntersections;
float subpixelCount = 4;
// we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
// region to align with the pixel grid.
float offset = 0.5f;
if (options.Antialias)
{
offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
subpixelCount = 4;
}
}
using (BrushApplicator<TPixel> applicator = brush.CreateApplicator(source, rect, options))
{
int scanlineWidth = maxX - minX;
using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
using (IMemoryOwner<float> bScanline = source.MemoryAllocator.Allocate<float>(scanlineWidth))
{
bool scanlineDirty = true;
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
Span<float> buffer = bBuffer.GetSpan();
Span<float> scanline = bScanline.GetSpan();
bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush);
TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel<TPixel>() : default;
for (int y = minY; y < maxY; y++)
{
if (scanlineDirty)
{
scanline.Clear();
scanlineDirty = false;
}
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
{
// nothing on this line, skip
continue;
}
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
nextX = Math.Max(nextX, 0);
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
if (scanlineDirty)
{
if (!options.Antialias)
{
bool hasOnes = false;
bool hasZeros = false;
for (int x = 0; x < scanlineWidth; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
hasOnes = true;
}
else
{
scanline[x] = 0;
hasZeros = true;
}
}
if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
{
if (hasOnes)
{
source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor);
}
continue;
}
}
applicator.Apply(scanline, minX, y);
}
}
}
}
}
private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush)
{
solidBrush = this.definition.Brush as SolidBrush;
if (solidBrush == null)
{
return false;
}
return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
}

430
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs

@ -2,30 +2,20 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Collections.Generic;
using SixLabors.Fonts;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Processors.Text
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// Defines a processor to draw text on an <see cref="Image"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DrawTextProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
public class DrawTextProcessor : IImageProcessor
{
private CachingGlyphRenderer textRenderer;
/// <summary>
/// Initializes a new instance of the <see cref="DrawTextProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="DrawTextProcessor"/> class.
/// </summary>
/// <param name="options">The options</param>
/// <param name="text">The text we want to render</param>
@ -33,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
/// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="pen">The pen to outline text with.</param>
/// <param name="location">The location on the image to start drawing the text from.</param>
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location)
{
Guard.NotNull(text, nameof(text));
Guard.NotNull(font, nameof(font));
@ -52,24 +42,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
}
/// <summary>
/// Gets the brush.
/// Gets the brush used to fill the glyphs.
/// </summary>
public IBrush<TPixel> Brush { get; }
public IBrush Brush { get; }
/// <summary>
/// Gets the options
/// Gets the <see cref="TextGraphicsOptions"/> defining blending modes and text-specific drawing settings.
/// </summary>
public TextGraphicsOptions Options { get; }
/// <summary>
/// Gets the text
/// Gets the text to draw.
/// </summary>
public string Text { get; }
/// <summary>
/// Gets the pen used for outlining the text, if Null then we will not outline
/// </summary>
public IPen<TPixel> Pen { get; }
public IPen Pen { get; }
/// <summary>
/// Gets the font used to render the text.
@ -81,403 +71,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
/// </summary>
public PointF Location { get; }
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
base.BeforeImageApply(source, sourceRectangle);
// do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{
ApplyKerning = this.Options.ApplyKerning,
TabWidth = this.Options.TabWidth,
WrappingWidth = this.Options.WrapTextWidth,
HorizontalAlignment = this.Options.HorizontalAlignment,
VerticalAlignment = this.Options.VerticalAlignment
};
this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), 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);
}
protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
base.AfterImageApply(source, sourceRectangle);
this.textRenderer?.Dispose();
this.textRenderer = null;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
// this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
Draw(this.textRenderer.FillOperations, this.Brush);
Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill);
void Draw(List<DrawingOperation> operations, IBrush<TPixel> brush)
{
if (operations?.Count > 0)
{
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options))
{
foreach (DrawingOperation operation in operations)
{
Buffer2D<float> buffer = operation.Map;
int startY = operation.Location.Y;
int startX = operation.Location.X;
int offSetSpan = 0;
if (startX < 0)
{
offSetSpan = -startX;
startX = 0;
}
int fistRow = 0;
if (startY < 0)
{
fistRow = -startY;
}
int maxHeight = source.Height - startY;
int end = Math.Min(operation.Map.Height, maxHeight);
for (int row = fistRow; row < end; row++)
{
int y = startY + row;
Span<float> span = buffer.GetRowSpan(row).Slice(offSetSpan);
app.Apply(span, startX, y);
}
}
}
}
}
}
private struct DrawingOperation
{
public Buffer2D<float> Map { get; set; }
public Point Location { get; set; }
}
private class CachingGlyphRenderer : IGlyphRenderer, IDisposable
{
// just enough accuracy to allow for 1/8 pixel differences which
// later are accumulated while rendering, but do not grow into full pixel offsets
// The value 8 is benchmarked to:
// - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant)
// - Cache hit ratio above 60%
private const float AccuracyMultiple = 8;
private readonly PathBuilder builder;
private Point currentRenderPosition = default;
private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default;
private readonly int offset = 0;
private PointF currentPoint = default(PointF);
private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>
glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>();
private readonly bool renderOutline = false;
private readonly bool renderFill = false;
private bool rasterizationRequired = false;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{
this.MemoryAllocator = memoryAllocator;
this.Pen = pen;
this.renderFill = renderFill;
this.renderOutline = pen != null;
this.offset = 2;
if (this.renderFill)
{
this.FillOperations = new List<DrawingOperation>(size);
}
if (this.renderOutline)
{
this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2);
this.OutlineOperations = new List<DrawingOperation>(size);
}
this.builder = new PathBuilder();
}
public List<DrawingOperation> FillOperations { get; }
public List<DrawingOperation> OutlineOperations { get; }
public MemoryAllocator MemoryAllocator { get; internal set; }
public IPen Pen { get; internal set; }
public GraphicsOptions Options { get; internal set; }
public void BeginFigure()
{
this.builder.StartFigure();
}
public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters)
{
this.currentRenderPosition = Point.Truncate(bounds.Location);
PointF subPixelOffset = bounds.Location - this.currentRenderPosition;
subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple;
subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple;
// we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate
this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset);
this.currentGlyphRenderParams = (parameters, subPixelOffset);
if (this.glyphData.ContainsKey(this.currentGlyphRenderParams))
{
// we have already drawn the glyph vectors skip trying again
this.rasterizationRequired = false;
return false;
}
// we check to see if we have a render cache and if we do then we render else
this.builder.Clear();
// ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back
this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset));
this.rasterizationRequired = true;
return true;
}
public void BeginText(RectangleF bounds)
{
// not concerned about this one
this.OutlineOperations?.Clear();
this.FillOperations?.Clear();
}
public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
this.currentPoint = point;
}
public void Dispose()
{
foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData)
{
kv.Value.Dispose();
}
this.glyphData.Clear();
}
public void EndFigure()
{
this.builder.CloseFigure();
}
public void EndGlyph()
{
GlyphRenderData renderData = default;
// has the glyoh been rendedered already????
if (this.rasterizationRequired)
{
IPath path = this.builder.Build();
if (this.renderFill)
{
renderData.FillMap = this.Render(path);
}
if (this.renderOutline)
{
if (this.Pen.StrokePattern.Length == 0)
{
path = path.GenerateOutline(this.Pen.StrokeWidth);
}
else
{
path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern);
}
renderData.OutlineMap = this.Render(path);
}
this.glyphData[this.currentGlyphRenderParams] = renderData;
}
else
{
renderData = this.glyphData[this.currentGlyphRenderParams];
}
if (this.renderFill)
{
this.FillOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.FillMap
});
}
if (this.renderOutline)
{
this.OutlineOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.OutlineMap
});
}
}
private Buffer2D<float> Render(IPath path)
{
Size size = Rectangle.Ceiling(path.Bounds).Size;
size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2));
float subpixelCount = 4;
float offset = 0.5f;
if (this.Options.Antialias)
{
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
subpixelCount = 4;
}
}
// take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it.
Buffer2D<float> fullBuffer = this.MemoryAllocator.Allocate2D<float>(size.Width + 1, size.Height + 1, AllocationOptions.Clean);
using (IMemoryOwner<float> bufferBacking = this.MemoryAllocator.Allocate<float>(path.MaxIntersections))
using (IMemoryOwner<PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate<PointF>(size.Width))
{
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (int y = 0; y <= size.Height; y++)
{
Span<float> scanline = fullBuffer.GetRowSpan(y);
bool scanlineDirty = false;
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
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)
{
// nothing on this line skip
continue;
}
for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++)
{
buffer[i] = intersectionSpan[i].X;
}
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point];
float scanEnd = buffer[point + 1];
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
nextX = Math.Max(nextX, 0);
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
if (scanlineDirty)
{
if (!this.Options.Antialias)
{
for (int x = 0; x < size.Width; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
}
else
{
scanline[x] = 0;
}
}
}
}
}
}
return fullBuffer;
}
public void EndText()
{
}
public void LineTo(PointF point)
{
this.builder.AddLine(this.currentPoint, point);
this.currentPoint = point;
}
public void MoveTo(PointF point)
{
this.builder.StartFigure();
this.currentPoint = point;
}
public void QuadraticBezierTo(PointF secondControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, point);
this.currentPoint = point;
}
private struct GlyphRenderData : IDisposable
{
public Buffer2D<float> FillMap;
public Buffer2D<float> OutlineMap;
public void Dispose()
{
this.FillMap?.Dispose();
this.OutlineMap?.Dispose();
}
}
return new DrawTextProcessor<TPixel>(this);
}
}
}

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

@ -0,0 +1,446 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Collections.Generic;
using SixLabors.Fonts;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Processors.Text
{
/// <summary>
/// Using the brush as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DrawTextProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private CachingGlyphRenderer textRenderer;
private readonly DrawTextProcessor definition;
public DrawTextProcessor(DrawTextProcessor definition)
{
this.definition = definition;
}
private TextGraphicsOptions Options => this.definition.Options;
private Font Font => this.definition.Font;
private PointF Location => this.definition.Location;
private string Text => this.definition.Text;
private IPen Pen => this.definition.Pen;
private IBrush Brush => this.definition.Brush;
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
base.BeforeImageApply(source, sourceRectangle);
// do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{
ApplyKerning = this.Options.ApplyKerning,
TabWidth = this.Options.TabWidth,
WrappingWidth = this.Options.WrapTextWidth,
HorizontalAlignment = this.Options.HorizontalAlignment,
VerticalAlignment = this.Options.VerticalAlignment
};
this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), 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);
}
protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
base.AfterImageApply(source, sourceRectangle);
this.textRenderer?.Dispose();
this.textRenderer = null;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
// this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
Draw(this.textRenderer.FillOperations, this.Brush);
Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill);
void Draw(List<DrawingOperation> operations, IBrush brush)
{
if (operations?.Count > 0)
{
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options))
{
foreach (DrawingOperation operation in operations)
{
Buffer2D<float> buffer = operation.Map;
int startY = operation.Location.Y;
int startX = operation.Location.X;
int offSetSpan = 0;
if (startX < 0)
{
offSetSpan = -startX;
startX = 0;
}
int fistRow = 0;
if (startY < 0)
{
fistRow = -startY;
}
int maxHeight = source.Height - startY;
int end = Math.Min(operation.Map.Height, maxHeight);
for (int row = fistRow; row < end; row++)
{
int y = startY + row;
Span<float> span = buffer.GetRowSpan(row).Slice(offSetSpan);
app.Apply(span, startX, y);
}
}
}
}
}
}
private struct DrawingOperation
{
public Buffer2D<float> Map { get; set; }
public Point Location { get; set; }
}
private class CachingGlyphRenderer : IGlyphRenderer, IDisposable
{
// just enough accuracy to allow for 1/8 pixel differences which
// later are accumulated while rendering, but do not grow into full pixel offsets
// The value 8 is benchmarked to:
// - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant)
// - Cache hit ratio above 60%
private const float AccuracyMultiple = 8;
private readonly PathBuilder builder;
private Point currentRenderPosition = default;
private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default;
private readonly int offset = 0;
private PointF currentPoint = default(PointF);
private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>
glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>();
private readonly bool renderOutline = false;
private readonly bool renderFill = false;
private bool rasterizationRequired = false;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{
this.MemoryAllocator = memoryAllocator;
this.Pen = pen;
this.renderFill = renderFill;
this.renderOutline = pen != null;
this.offset = 2;
if (this.renderFill)
{
this.FillOperations = new List<DrawingOperation>(size);
}
if (this.renderOutline)
{
this.offset = (int)MathF.Ceiling((pen.StrokeWidth * 2) + 2);
this.OutlineOperations = new List<DrawingOperation>(size);
}
this.builder = new PathBuilder();
}
public List<DrawingOperation> FillOperations { get; }
public List<DrawingOperation> OutlineOperations { get; }
public MemoryAllocator MemoryAllocator { get; internal set; }
public IPen Pen { get; internal set; }
public GraphicsOptions Options { get; internal set; }
public void BeginFigure()
{
this.builder.StartFigure();
}
public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters)
{
this.currentRenderPosition = Point.Truncate(bounds.Location);
PointF subPixelOffset = bounds.Location - this.currentRenderPosition;
subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple;
subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple;
// we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate
this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset);
this.currentGlyphRenderParams = (parameters, subPixelOffset);
if (this.glyphData.ContainsKey(this.currentGlyphRenderParams))
{
// we have already drawn the glyph vectors skip trying again
this.rasterizationRequired = false;
return false;
}
// we check to see if we have a render cache and if we do then we render else
this.builder.Clear();
// ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back
this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset));
this.rasterizationRequired = true;
return true;
}
public void BeginText(RectangleF bounds)
{
// not concerned about this one
this.OutlineOperations?.Clear();
this.FillOperations?.Clear();
}
public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
this.currentPoint = point;
}
public void Dispose()
{
foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData)
{
kv.Value.Dispose();
}
this.glyphData.Clear();
}
public void EndFigure()
{
this.builder.CloseFigure();
}
public void EndGlyph()
{
GlyphRenderData renderData = default;
// has the glyoh been rendedered already????
if (this.rasterizationRequired)
{
IPath path = this.builder.Build();
if (this.renderFill)
{
renderData.FillMap = this.Render(path);
}
if (this.renderOutline)
{
if (this.Pen.StrokePattern.Length == 0)
{
path = path.GenerateOutline(this.Pen.StrokeWidth);
}
else
{
path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern);
}
renderData.OutlineMap = this.Render(path);
}
this.glyphData[this.currentGlyphRenderParams] = renderData;
}
else
{
renderData = this.glyphData[this.currentGlyphRenderParams];
}
if (this.renderFill)
{
this.FillOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.FillMap
});
}
if (this.renderOutline)
{
this.OutlineOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.OutlineMap
});
}
}
private Buffer2D<float> Render(IPath path)
{
Size size = Rectangle.Ceiling(path.Bounds).Size;
size = new Size(size.Width + (this.offset * 2), size.Height + (this.offset * 2));
float subpixelCount = 4;
float offset = 0.5f;
if (this.Options.Antialias)
{
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
subpixelCount = 4;
}
}
// take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it.
Buffer2D<float> fullBuffer = this.MemoryAllocator.Allocate2D<float>(size.Width + 1, size.Height + 1, AllocationOptions.Clean);
using (IMemoryOwner<float> bufferBacking = this.MemoryAllocator.Allocate<float>(path.MaxIntersections))
using (IMemoryOwner<PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate<PointF>(size.Width))
{
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (int y = 0; y <= size.Height; y++)
{
Span<float> scanline = fullBuffer.GetRowSpan(y);
bool scanlineDirty = false;
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
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)
{
// nothing on this line skip
continue;
}
for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++)
{
buffer[i] = intersectionSpan[i].X;
}
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
{
// points will be paired up
float scanStart = buffer[point];
float scanEnd = buffer[point + 1];
int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd + offset);
if (startX >= 0 && startX < scanline.Length)
{
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
if (endX >= 0 && endX < scanline.Length)
{
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
nextX = Math.Max(nextX, 0);
for (int x = nextX; x < endX; x++)
{
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
}
}
if (scanlineDirty)
{
if (!this.Options.Antialias)
{
for (int x = 0; x < size.Width; x++)
{
if (scanline[x] >= 0.5)
{
scanline[x] = 1;
}
else
{
scanline[x] = 0;
}
}
}
}
}
}
return fullBuffer;
}
public void EndText()
{
}
public void LineTo(PointF point)
{
this.builder.AddLine(this.currentPoint, point);
this.currentPoint = point;
}
public void MoveTo(PointF point)
{
this.builder.StartFigure();
this.currentPoint = point;
}
public void QuadraticBezierTo(PointF secondControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, point);
this.currentPoint = point;
}
private struct GlyphRenderData : IDisposable
{
public Buffer2D<float> FillMap;
public Buffer2D<float> OutlineMap;
public void Dispose()
{
this.FillMap?.Dispose();
this.OutlineMap?.Dispose();
}
}
}
}
}

21
src/ImageSharp.Drawing/Processing/RadialGradientBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs

@ -11,15 +11,13 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// A Circular Gradient Brush, defined by center point and radius.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class RadialGradientBrush<TPixel> : GradientBrushBase<TPixel>
where TPixel : struct, IPixel<TPixel>
public sealed class RadialGradientBrush : GradientBrushBase
{
private readonly PointF center;
private readonly float radius;
/// <inheritdoc cref="GradientBrushBase{TPixel}" />
/// <inheritdoc cref="GradientBrushBase" />
/// <param name="center">The center of the circular gradient and 0 for the color stops.</param>
/// <param name="radius">The radius of the circular gradient and 1 for the color stops.</param>
/// <param name="repetitionMode">Defines how the colors in the gradient are repeated.</param>
@ -28,19 +26,19 @@ namespace SixLabors.ImageSharp.Processing
PointF center,
float radius,
GradientRepetitionMode repetitionMode,
params ColorStop<TPixel>[] colorStops)
params ColorStop[] colorStops)
: base(repetitionMode, colorStops)
{
this.center = center;
this.radius = radius;
}
/// <inheritdoc cref="CreateApplicator" />
public override BrushApplicator<TPixel> CreateApplicator(
/// <inheritdoc />
public override BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options) =>
new RadialGradientBrushApplicator(
new RadialGradientBrushApplicator<TPixel>(
source,
options,
this.center,
@ -49,14 +47,15 @@ namespace SixLabors.ImageSharp.Processing
this.RepetitionMode);
/// <inheritdoc />
private sealed class RadialGradientBrushApplicator : GradientBrushApplicatorBase
private sealed class RadialGradientBrushApplicator<TPixel> : GradientBrushApplicatorBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly PointF center;
private readonly float radius;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushApplicator" /> class.
/// 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>
@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing
GraphicsOptions options,
PointF center,
float radius,
ColorStop<TPixel>[] colorStops,
ColorStop[] colorStops,
GradientRepetitionMode repetitionMode)
: base(target, options, colorStops, repetitionMode)
{

30
src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/RecolorBrush.cs

@ -15,17 +15,15 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Provides an implementation of a brush that can recolor an image
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class RecolorBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
public class RecolorBrush : IBrush
{
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrush{TPixel}" /> class.
/// Initializes a new instance of the <see cref="RecolorBrush" /> class.
/// </summary>
/// <param name="sourceColor">Color of the source.</param>
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold as a value between 0 and 1.</param>
public RecolorBrush(TPixel sourceColor, TPixel targetColor, float threshold)
public RecolorBrush(Color sourceColor, Color targetColor, float threshold)
{
this.SourceColor = sourceColor;
this.Threshold = threshold;
@ -43,23 +41,33 @@ namespace SixLabors.ImageSharp.Processing
/// <value>
/// The color of the source.
/// </value>
public TPixel SourceColor { get; }
public Color SourceColor { get; }
/// <summary>
/// Gets the target color.
/// </summary>
public TPixel TargetColor { get; }
public Color TargetColor { get; }
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator<TPixel>(
ImageFrame<TPixel> source,
RectangleF region,
GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return new RecolorBrushApplicator(source, this.SourceColor, this.TargetColor, this.Threshold, options);
return new RecolorBrushApplicator<TPixel>(
source,
this.SourceColor.ToPixel<TPixel>(),
this.TargetColor.ToPixel<TPixel>(),
this.Threshold,
options);
}
/// <summary>
/// The recolor brush applicator.
/// </summary>
private class RecolorBrushApplicator : BrushApplicator<TPixel>
private class RecolorBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// The source color.
@ -79,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing
private readonly TPixel targetColorPixel;
/// <summary>
/// Initializes a new instance of the <see cref="RecolorBrushApplicator" /> class.
/// Initializes a new instance of the <see cref="RecolorBrushApplicator{TPixel}" /> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="sourceColor">Color of the source.</param>

22
src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs → src/ImageSharp.Drawing/Processing/SolidBrush.cs

@ -15,20 +15,18 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Provides an implementation of a solid brush for painting solid color areas.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class SolidBrush<TPixel> : IBrush<TPixel>
where TPixel : struct, IPixel<TPixel>
public class SolidBrush : IBrush
{
/// <summary>
/// The color to paint.
/// </summary>
private readonly TPixel color;
private readonly Color color;
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrush{TPixel}"/> class.
/// Initializes a new instance of the <see cref="SolidBrush"/> class.
/// </summary>
/// <param name="color">The color.</param>
public SolidBrush(TPixel color)
public SolidBrush(Color color)
{
this.color = color;
}
@ -39,21 +37,23 @@ namespace SixLabors.ImageSharp.Processing
/// <value>
/// The color.
/// </value>
public TPixel Color => this.color;
public Color Color => this.color;
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
public BrushApplicator<TPixel> CreateApplicator<TPixel>(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return new SolidBrushApplicator(source, this.color, options);
return new SolidBrushApplicator<TPixel>(source, this.color.ToPixel<TPixel>(), options);
}
/// <summary>
/// The solid brush applicator.
/// </summary>
private class SolidBrushApplicator : BrushApplicator<TPixel>
private class SolidBrushApplicator<TPixel> : BrushApplicator<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class.
/// Initializes a new instance of the <see cref="SolidBrushApplicator{TPixel}"/> class.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="color">The color.</param>

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

@ -1,169 +1,163 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct TextGraphicsOptions
{
private const int DefaultTextDpi = 72;
/// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>.
/// </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 PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode;
private float wrapTextWidth;
private HorizontalAlignment? horizontalAlignment;
private VerticalAlignment? verticalAlignment;
/// <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)
{
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;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; }
/// <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; }
/// <summary>
/// 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; }
// 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.
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get => this.colorBlendingMode; set => this.colorBlendingMode = value; }
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get => this.alphaCompositionMode; set => this.alphaCompositionMode = value; }
/// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
/// </summary>
public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; }
/// <summary>
/// Gets or sets a value indicating the number of space widths a tab should lock to.
/// </summary>
public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; }
/// <summary>
/// Gets or sets a value indicating if greater than zero determine the width at which text should wrap.
/// </summary>
public float WrapTextWidth { get => this.wrapTextWidth; set => this.wrapTextWidth = value; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the X axis.
/// </summary>
public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the Y axis.
/// </summary>
public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => 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.
/// </summary>
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; }
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; }
/// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{
return new TextGraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage,
colorBlendingMode = options.ColorBlendingMode,
alphaCompositionMode = options.AlphaCompositionMode
};
}
/// <summary>
/// Performs an explicit conversion from <see cref="TextGraphicsOptions"/> to <see cref="GraphicsOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{
return new GraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct TextGraphicsOptions
{
private const int DefaultTextDpi = 72;
/// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>.
/// </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;
/// <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)
{
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;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; }
/// <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; }
/// <summary>
/// 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; }
// 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.
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; set; }
/// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
/// </summary>
public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; }
/// <summary>
/// Gets or sets a value indicating the number of space widths a tab should lock to.
/// </summary>
public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; }
/// <summary>
/// Gets or sets a value indicating if greater than zero determine the width at which text should wrap.
/// </summary>
public float WrapTextWidth { get; set; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the X axis.
/// </summary>
public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; }
/// <summary>
/// Gets or sets a value indicating the DPI to render text along the Y axis.
/// </summary>
public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => 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.
/// </summary>
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; }
/// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; }
/// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{
return new TextGraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage,
ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode
};
}
/// <summary>
/// Performs an explicit conversion from <see cref="TextGraphicsOptions"/> to <see cref="GraphicsOptions"/>.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{
return new GraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode,
BlendPercentage = options.BlendPercentage
};
}
}
AlphaCompositionMode = options.AlphaCompositionMode,
BlendPercentage = options.BlendPercentage
};
}
}
}

17
src/ImageSharp/Color/Color.Conversions.cs

@ -62,8 +62,20 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
public Color(Vector4 vector) => this.data = new Rgba64(vector);
/// <summary>
/// Converts a <see cref="Color"/> to <see cref="Vector4"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/>.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static explicit operator Vector4(Color color) => color.data.ToVector4();
/// <summary>
/// Converts an <see cref="Vector4"/> to <see cref="Color"/>.
/// </summary>
/// <param name="source">The <see cref="Vector4"/>.</param>
/// <returns>The <see cref="Color"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
internal Rgba64 ToRgba64() => this.data;
public static explicit operator Color(Vector4 source) => new Color(source);
[MethodImpl(InliningOptions.ShortMethod)]
internal Rgba32 ToRgba32() => this.data.ToRgba32();
@ -79,5 +91,8 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
internal Bgr24 ToBgr24() => this.data.ToBgr24();
[MethodImpl(InliningOptions.ShortMethod)]
internal Vector4 ToVector4() => this.data.ToVector4();
}
}

12
src/ImageSharp/Color/Color.cs

@ -119,6 +119,18 @@ namespace SixLabors.ImageSharp
return new Color(rgba);
}
/// <summary>
/// Alters the alpha channel of the color, returning a new instance.
/// </summary>
/// <param name="alpha">The new value of alpha [0..1].</param>
/// <returns>The color having it's alpha channel altered.</returns>
public Color WithAlpha(float alpha)
{
Vector4 v = (Vector4)this;
v.W = alpha;
return new Color(v);
}
/// <summary>
/// Gets the hexadecimal representation of the color instance in rrggbbaa form.
/// </summary>

232
src/ImageSharp/GraphicsOptions.cs

@ -1,147 +1,146 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct 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;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct 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)
{
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;
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>
/// <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)
/// <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;
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>
/// <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>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
/// <param name="blending">color blending mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
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>
/// <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)
/// <param name="composition">alpha composition mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = composition;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias
{
get => this.antialias ?? true;
set => this.antialias = value;
}
/// <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;
}
/// <summary>
/// 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;
}
// 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.
/// <summary>
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
this.alphaCompositionMode = composition;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode
{
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias
{
get => this.antialias ?? true;
set => this.antialias = value;
}
/// <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;
}
/// <summary>
/// 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;
}
// 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.
/// <summary>
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
}
/// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode
{
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
}
/// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </summary>
/// <typeparam name="TPixel">The pixel format</typeparam>
/// <param name="color">the color</param>
/// <returns>true if the color can be considered opaque</returns>
/// <remarks>
@ -149,8 +148,7 @@ namespace SixLabors.ImageSharp
/// 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<TPixel>(TPixel color)
where TPixel : struct, IPixel<TPixel>
internal bool IsOpaqueColorWithoutBlending(Color color)
{
if (this.ColorBlendingMode != PixelColorBlendingMode.Normal)
{
@ -174,6 +172,6 @@ namespace SixLabors.ImageSharp
}
return true;
}
}
}
}
}

17
src/ImageSharp/Image.cs

@ -88,6 +88,23 @@ namespace SixLabors.ImageSharp
this.AcceptVisitor(visitor);
}
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <returns>The <see cref="Image{TPixel2}"/></returns>
public Image<TPixel2> CloneAs<TPixel2>()
where TPixel2 : struct, IPixel<TPixel2> => this.CloneAs<TPixel2>(this.Configuration);
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <returns>The <see cref="Image{TPixel2}"/>.</returns>
public abstract Image<TPixel2> CloneAs<TPixel2>(Configuration configuration)
where TPixel2 : struct, IPixel<TPixel2>;
/// <summary>
/// Accept a <see cref="IImageVisitor"/>.
/// Implemented by <see cref="Image{TPixel}"/> invoking <see cref="IImageVisitor.Visit{TPixel}"/>

2
src/ImageSharp/ImageSharp.csproj

@ -40,7 +40,7 @@
<ItemGroup>
<AdditionalFiles Include="..\..\standards\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-rc.114" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-dev000101" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
</ItemGroup>

11
src/ImageSharp/Image{TPixel}.cs

@ -153,22 +153,13 @@ namespace SixLabors.ImageSharp
return new Image<TPixel>(configuration, this.Metadata.DeepClone(), clonedFrames);
}
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <returns>The <see cref="Image{TPixel2}"/></returns>
public Image<TPixel2> CloneAs<TPixel2>()
where TPixel2 : struct, IPixel<TPixel2> => this.CloneAs<TPixel2>(this.Configuration);
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <returns>The <see cref="Image{TPixel2}"/>.</returns>
public Image<TPixel2> CloneAs<TPixel2>(Configuration configuration)
where TPixel2 : struct, IPixel<TPixel2>
public override Image<TPixel2> CloneAs<TPixel2>(Configuration configuration)
{
IEnumerable<ImageFrame<TPixel2>> clonedFrames = this.Frames.Select(x => x.CloneAs<TPixel2>(configuration));
return new Image<TPixel2>(configuration, this.Metadata.DeepClone(), clonedFrames);

284
src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs

@ -11,711 +11,711 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F0F8FF.
/// </summary>
public static readonly Rgba32 AliceBlue = NamedColors<Rgba32>.AliceBlue;
public static readonly Rgba32 AliceBlue = Color.AliceBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FAEBD7.
/// </summary>
public static readonly Rgba32 AntiqueWhite = NamedColors<Rgba32>.AntiqueWhite;
public static readonly Rgba32 AntiqueWhite = Color.AntiqueWhite;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00FFFF.
/// </summary>
public static readonly Rgba32 Aqua = NamedColors<Rgba32>.Aqua;
public static readonly Rgba32 Aqua = Color.Aqua;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #7FFFD4.
/// </summary>
public static readonly Rgba32 Aquamarine = NamedColors<Rgba32>.Aquamarine;
public static readonly Rgba32 Aquamarine = Color.Aquamarine;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F0FFFF.
/// </summary>
public static readonly Rgba32 Azure = NamedColors<Rgba32>.Azure;
public static readonly Rgba32 Azure = Color.Azure;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F5F5DC.
/// </summary>
public static readonly Rgba32 Beige = NamedColors<Rgba32>.Beige;
public static readonly Rgba32 Beige = Color.Beige;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFE4C4.
/// </summary>
public static readonly Rgba32 Bisque = NamedColors<Rgba32>.Bisque;
public static readonly Rgba32 Bisque = Color.Bisque;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #000000.
/// </summary>
public static readonly Rgba32 Black = NamedColors<Rgba32>.Black;
public static readonly Rgba32 Black = Color.Black;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFEBCD.
/// </summary>
public static readonly Rgba32 BlanchedAlmond = NamedColors<Rgba32>.BlanchedAlmond;
public static readonly Rgba32 BlanchedAlmond = Color.BlanchedAlmond;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #0000FF.
/// </summary>
public static readonly Rgba32 Blue = NamedColors<Rgba32>.Blue;
public static readonly Rgba32 Blue = Color.Blue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #8A2BE2.
/// </summary>
public static readonly Rgba32 BlueViolet = NamedColors<Rgba32>.BlueViolet;
public static readonly Rgba32 BlueViolet = Color.BlueViolet;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #A52A2A.
/// </summary>
public static readonly Rgba32 Brown = NamedColors<Rgba32>.Brown;
public static readonly Rgba32 Brown = Color.Brown;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DEB887.
/// </summary>
public static readonly Rgba32 BurlyWood = NamedColors<Rgba32>.BurlyWood;
public static readonly Rgba32 BurlyWood = Color.BurlyWood;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #5F9EA0.
/// </summary>
public static readonly Rgba32 CadetBlue = NamedColors<Rgba32>.CadetBlue;
public static readonly Rgba32 CadetBlue = Color.CadetBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #7FFF00.
/// </summary>
public static readonly Rgba32 Chartreuse = NamedColors<Rgba32>.Chartreuse;
public static readonly Rgba32 Chartreuse = Color.Chartreuse;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #D2691E.
/// </summary>
public static readonly Rgba32 Chocolate = NamedColors<Rgba32>.Chocolate;
public static readonly Rgba32 Chocolate = Color.Chocolate;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF7F50.
/// </summary>
public static readonly Rgba32 Coral = NamedColors<Rgba32>.Coral;
public static readonly Rgba32 Coral = Color.Coral;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #6495ED.
/// </summary>
public static readonly Rgba32 CornflowerBlue = NamedColors<Rgba32>.CornflowerBlue;
public static readonly Rgba32 CornflowerBlue = Color.CornflowerBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFF8DC.
/// </summary>
public static readonly Rgba32 Cornsilk = NamedColors<Rgba32>.Cornsilk;
public static readonly Rgba32 Cornsilk = Color.Cornsilk;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DC143C.
/// </summary>
public static readonly Rgba32 Crimson = NamedColors<Rgba32>.Crimson;
public static readonly Rgba32 Crimson = Color.Crimson;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00FFFF.
/// </summary>
public static readonly Rgba32 Cyan = NamedColors<Rgba32>.Cyan;
public static readonly Rgba32 Cyan = Color.Cyan;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00008B.
/// </summary>
public static readonly Rgba32 DarkBlue = NamedColors<Rgba32>.DarkBlue;
public static readonly Rgba32 DarkBlue = Color.DarkBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #008B8B.
/// </summary>
public static readonly Rgba32 DarkCyan = NamedColors<Rgba32>.DarkCyan;
public static readonly Rgba32 DarkCyan = Color.DarkCyan;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #B8860B.
/// </summary>
public static readonly Rgba32 DarkGoldenrod = NamedColors<Rgba32>.DarkGoldenrod;
public static readonly Rgba32 DarkGoldenrod = Color.DarkGoldenrod;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #A9A9A9.
/// </summary>
public static readonly Rgba32 DarkGray = NamedColors<Rgba32>.DarkGray;
public static readonly Rgba32 DarkGray = Color.DarkGray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #006400.
/// </summary>
public static readonly Rgba32 DarkGreen = NamedColors<Rgba32>.DarkGreen;
public static readonly Rgba32 DarkGreen = Color.DarkGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #BDB76B.
/// </summary>
public static readonly Rgba32 DarkKhaki = NamedColors<Rgba32>.DarkKhaki;
public static readonly Rgba32 DarkKhaki = Color.DarkKhaki;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #8B008B.
/// </summary>
public static readonly Rgba32 DarkMagenta = NamedColors<Rgba32>.DarkMagenta;
public static readonly Rgba32 DarkMagenta = Color.DarkMagenta;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #556B2F.
/// </summary>
public static readonly Rgba32 DarkOliveGreen = NamedColors<Rgba32>.DarkOliveGreen;
public static readonly Rgba32 DarkOliveGreen = Color.DarkOliveGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF8C00.
/// </summary>
public static readonly Rgba32 DarkOrange = NamedColors<Rgba32>.DarkOrange;
public static readonly Rgba32 DarkOrange = Color.DarkOrange;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #9932CC.
/// </summary>
public static readonly Rgba32 DarkOrchid = NamedColors<Rgba32>.DarkOrchid;
public static readonly Rgba32 DarkOrchid = Color.DarkOrchid;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #8B0000.
/// </summary>
public static readonly Rgba32 DarkRed = NamedColors<Rgba32>.DarkRed;
public static readonly Rgba32 DarkRed = Color.DarkRed;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #E9967A.
/// </summary>
public static readonly Rgba32 DarkSalmon = NamedColors<Rgba32>.DarkSalmon;
public static readonly Rgba32 DarkSalmon = Color.DarkSalmon;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #8FBC8B.
/// </summary>
public static readonly Rgba32 DarkSeaGreen = NamedColors<Rgba32>.DarkSeaGreen;
public static readonly Rgba32 DarkSeaGreen = Color.DarkSeaGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #483D8B.
/// </summary>
public static readonly Rgba32 DarkSlateBlue = NamedColors<Rgba32>.DarkSlateBlue;
public static readonly Rgba32 DarkSlateBlue = Color.DarkSlateBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #2F4F4F.
/// </summary>
public static readonly Rgba32 DarkSlateGray = NamedColors<Rgba32>.DarkSlateGray;
public static readonly Rgba32 DarkSlateGray = Color.DarkSlateGray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00CED1.
/// </summary>
public static readonly Rgba32 DarkTurquoise = NamedColors<Rgba32>.DarkTurquoise;
public static readonly Rgba32 DarkTurquoise = Color.DarkTurquoise;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #9400D3.
/// </summary>
public static readonly Rgba32 DarkViolet = NamedColors<Rgba32>.DarkViolet;
public static readonly Rgba32 DarkViolet = Color.DarkViolet;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF1493.
/// </summary>
public static readonly Rgba32 DeepPink = NamedColors<Rgba32>.DeepPink;
public static readonly Rgba32 DeepPink = Color.DeepPink;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00BFFF.
/// </summary>
public static readonly Rgba32 DeepSkyBlue = NamedColors<Rgba32>.DeepSkyBlue;
public static readonly Rgba32 DeepSkyBlue = Color.DeepSkyBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #696969.
/// </summary>
public static readonly Rgba32 DimGray = NamedColors<Rgba32>.DimGray;
public static readonly Rgba32 DimGray = Color.DimGray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #1E90FF.
/// </summary>
public static readonly Rgba32 DodgerBlue = NamedColors<Rgba32>.DodgerBlue;
public static readonly Rgba32 DodgerBlue = Color.DodgerBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #B22222.
/// </summary>
public static readonly Rgba32 Firebrick = NamedColors<Rgba32>.Firebrick;
public static readonly Rgba32 Firebrick = Color.Firebrick;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFAF0.
/// </summary>
public static readonly Rgba32 FloralWhite = NamedColors<Rgba32>.FloralWhite;
public static readonly Rgba32 FloralWhite = Color.FloralWhite;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #228B22.
/// </summary>
public static readonly Rgba32 ForestGreen = NamedColors<Rgba32>.ForestGreen;
public static readonly Rgba32 ForestGreen = Color.ForestGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF00FF.
/// </summary>
public static readonly Rgba32 Fuchsia = NamedColors<Rgba32>.Fuchsia;
public static readonly Rgba32 Fuchsia = Color.Fuchsia;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DCDCDC.
/// </summary>
public static readonly Rgba32 Gainsboro = NamedColors<Rgba32>.Gainsboro;
public static readonly Rgba32 Gainsboro = Color.Gainsboro;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F8F8FF.
/// </summary>
public static readonly Rgba32 GhostWhite = NamedColors<Rgba32>.GhostWhite;
public static readonly Rgba32 GhostWhite = Color.GhostWhite;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFD700.
/// </summary>
public static readonly Rgba32 Gold = NamedColors<Rgba32>.Gold;
public static readonly Rgba32 Gold = Color.Gold;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DAA520.
/// </summary>
public static readonly Rgba32 Goldenrod = NamedColors<Rgba32>.Goldenrod;
public static readonly Rgba32 Goldenrod = Color.Goldenrod;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #808080.
/// </summary>
public static readonly Rgba32 Gray = NamedColors<Rgba32>.Gray;
public static readonly Rgba32 Gray = Color.Gray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #008000.
/// </summary>
public static readonly Rgba32 Green = NamedColors<Rgba32>.Green;
public static readonly Rgba32 Green = Color.Green;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #ADFF2F.
/// </summary>
public static readonly Rgba32 GreenYellow = NamedColors<Rgba32>.GreenYellow;
public static readonly Rgba32 GreenYellow = Color.GreenYellow;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F0FFF0.
/// </summary>
public static readonly Rgba32 Honeydew = NamedColors<Rgba32>.Honeydew;
public static readonly Rgba32 Honeydew = Color.Honeydew;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF69B4.
/// </summary>
public static readonly Rgba32 HotPink = NamedColors<Rgba32>.HotPink;
public static readonly Rgba32 HotPink = Color.HotPink;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #CD5C5C.
/// </summary>
public static readonly Rgba32 IndianRed = NamedColors<Rgba32>.IndianRed;
public static readonly Rgba32 IndianRed = Color.IndianRed;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #4B0082.
/// </summary>
public static readonly Rgba32 Indigo = NamedColors<Rgba32>.Indigo;
public static readonly Rgba32 Indigo = Color.Indigo;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFFF0.
/// </summary>
public static readonly Rgba32 Ivory = NamedColors<Rgba32>.Ivory;
public static readonly Rgba32 Ivory = Color.Ivory;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F0E68C.
/// </summary>
public static readonly Rgba32 Khaki = NamedColors<Rgba32>.Khaki;
public static readonly Rgba32 Khaki = Color.Khaki;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #E6E6FA.
/// </summary>
public static readonly Rgba32 Lavender = NamedColors<Rgba32>.Lavender;
public static readonly Rgba32 Lavender = Color.Lavender;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFF0F5.
/// </summary>
public static readonly Rgba32 LavenderBlush = NamedColors<Rgba32>.LavenderBlush;
public static readonly Rgba32 LavenderBlush = Color.LavenderBlush;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #7CFC00.
/// </summary>
public static readonly Rgba32 LawnGreen = NamedColors<Rgba32>.LawnGreen;
public static readonly Rgba32 LawnGreen = Color.LawnGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFACD.
/// </summary>
public static readonly Rgba32 LemonChiffon = NamedColors<Rgba32>.LemonChiffon;
public static readonly Rgba32 LemonChiffon = Color.LemonChiffon;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #ADD8E6.
/// </summary>
public static readonly Rgba32 LightBlue = NamedColors<Rgba32>.LightBlue;
public static readonly Rgba32 LightBlue = Color.LightBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F08080.
/// </summary>
public static readonly Rgba32 LightCoral = NamedColors<Rgba32>.LightCoral;
public static readonly Rgba32 LightCoral = Color.LightCoral;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #E0FFFF.
/// </summary>
public static readonly Rgba32 LightCyan = NamedColors<Rgba32>.LightCyan;
public static readonly Rgba32 LightCyan = Color.LightCyan;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FAFAD2.
/// </summary>
public static readonly Rgba32 LightGoldenrodYellow = NamedColors<Rgba32>.LightGoldenrodYellow;
public static readonly Rgba32 LightGoldenrodYellow = Color.LightGoldenrodYellow;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #D3D3D3.
/// </summary>
public static readonly Rgba32 LightGray = NamedColors<Rgba32>.LightGray;
public static readonly Rgba32 LightGray = Color.LightGray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #90EE90.
/// </summary>
public static readonly Rgba32 LightGreen = NamedColors<Rgba32>.LightGreen;
public static readonly Rgba32 LightGreen = Color.LightGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFB6C1.
/// </summary>
public static readonly Rgba32 LightPink = NamedColors<Rgba32>.LightPink;
public static readonly Rgba32 LightPink = Color.LightPink;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFA07A.
/// </summary>
public static readonly Rgba32 LightSalmon = NamedColors<Rgba32>.LightSalmon;
public static readonly Rgba32 LightSalmon = Color.LightSalmon;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #20B2AA.
/// </summary>
public static readonly Rgba32 LightSeaGreen = NamedColors<Rgba32>.LightSeaGreen;
public static readonly Rgba32 LightSeaGreen = Color.LightSeaGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #87CEFA.
/// </summary>
public static readonly Rgba32 LightSkyBlue = NamedColors<Rgba32>.LightSkyBlue;
public static readonly Rgba32 LightSkyBlue = Color.LightSkyBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #778899.
/// </summary>
public static readonly Rgba32 LightSlateGray = NamedColors<Rgba32>.LightSlateGray;
public static readonly Rgba32 LightSlateGray = Color.LightSlateGray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #B0C4DE.
/// </summary>
public static readonly Rgba32 LightSteelBlue = NamedColors<Rgba32>.LightSteelBlue;
public static readonly Rgba32 LightSteelBlue = Color.LightSteelBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFFE0.
/// </summary>
public static readonly Rgba32 LightYellow = NamedColors<Rgba32>.LightYellow;
public static readonly Rgba32 LightYellow = Color.LightYellow;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00FF00.
/// </summary>
public static readonly Rgba32 Lime = NamedColors<Rgba32>.Lime;
public static readonly Rgba32 Lime = Color.Lime;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #32CD32.
/// </summary>
public static readonly Rgba32 LimeGreen = NamedColors<Rgba32>.LimeGreen;
public static readonly Rgba32 LimeGreen = Color.LimeGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FAF0E6.
/// </summary>
public static readonly Rgba32 Linen = NamedColors<Rgba32>.Linen;
public static readonly Rgba32 Linen = Color.Linen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF00FF.
/// </summary>
public static readonly Rgba32 Magenta = NamedColors<Rgba32>.Magenta;
public static readonly Rgba32 Magenta = Color.Magenta;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #800000.
/// </summary>
public static readonly Rgba32 Maroon = NamedColors<Rgba32>.Maroon;
public static readonly Rgba32 Maroon = Color.Maroon;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #66CDAA.
/// </summary>
public static readonly Rgba32 MediumAquamarine = NamedColors<Rgba32>.MediumAquamarine;
public static readonly Rgba32 MediumAquamarine = Color.MediumAquamarine;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #0000CD.
/// </summary>
public static readonly Rgba32 MediumBlue = NamedColors<Rgba32>.MediumBlue;
public static readonly Rgba32 MediumBlue = Color.MediumBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #BA55D3.
/// </summary>
public static readonly Rgba32 MediumOrchid = NamedColors<Rgba32>.MediumOrchid;
public static readonly Rgba32 MediumOrchid = Color.MediumOrchid;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #9370DB.
/// </summary>
public static readonly Rgba32 MediumPurple = NamedColors<Rgba32>.MediumPurple;
public static readonly Rgba32 MediumPurple = Color.MediumPurple;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #3CB371.
/// </summary>
public static readonly Rgba32 MediumSeaGreen = NamedColors<Rgba32>.MediumSeaGreen;
public static readonly Rgba32 MediumSeaGreen = Color.MediumSeaGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #7B68EE.
/// </summary>
public static readonly Rgba32 MediumSlateBlue = NamedColors<Rgba32>.MediumSlateBlue;
public static readonly Rgba32 MediumSlateBlue = Color.MediumSlateBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00FA9A.
/// </summary>
public static readonly Rgba32 MediumSpringGreen = NamedColors<Rgba32>.MediumSpringGreen;
public static readonly Rgba32 MediumSpringGreen = Color.MediumSpringGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #48D1CC.
/// </summary>
public static readonly Rgba32 MediumTurquoise = NamedColors<Rgba32>.MediumTurquoise;
public static readonly Rgba32 MediumTurquoise = Color.MediumTurquoise;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #C71585.
/// </summary>
public static readonly Rgba32 MediumVioletRed = NamedColors<Rgba32>.MediumVioletRed;
public static readonly Rgba32 MediumVioletRed = Color.MediumVioletRed;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #191970.
/// </summary>
public static readonly Rgba32 MidnightBlue = NamedColors<Rgba32>.MidnightBlue;
public static readonly Rgba32 MidnightBlue = Color.MidnightBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F5FFFA.
/// </summary>
public static readonly Rgba32 MintCream = NamedColors<Rgba32>.MintCream;
public static readonly Rgba32 MintCream = Color.MintCream;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFE4E1.
/// </summary>
public static readonly Rgba32 MistyRose = NamedColors<Rgba32>.MistyRose;
public static readonly Rgba32 MistyRose = Color.MistyRose;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFE4B5.
/// </summary>
public static readonly Rgba32 Moccasin = NamedColors<Rgba32>.Moccasin;
public static readonly Rgba32 Moccasin = Color.Moccasin;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFDEAD.
/// </summary>
public static readonly Rgba32 NavajoWhite = NamedColors<Rgba32>.NavajoWhite;
public static readonly Rgba32 NavajoWhite = Color.NavajoWhite;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #000080.
/// </summary>
public static readonly Rgba32 Navy = NamedColors<Rgba32>.Navy;
public static readonly Rgba32 Navy = Color.Navy;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FDF5E6.
/// </summary>
public static readonly Rgba32 OldLace = NamedColors<Rgba32>.OldLace;
public static readonly Rgba32 OldLace = Color.OldLace;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #808000.
/// </summary>
public static readonly Rgba32 Olive = NamedColors<Rgba32>.Olive;
public static readonly Rgba32 Olive = Color.Olive;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #6B8E23.
/// </summary>
public static readonly Rgba32 OliveDrab = NamedColors<Rgba32>.OliveDrab;
public static readonly Rgba32 OliveDrab = Color.OliveDrab;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFA500.
/// </summary>
public static readonly Rgba32 Orange = NamedColors<Rgba32>.Orange;
public static readonly Rgba32 Orange = Color.Orange;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF4500.
/// </summary>
public static readonly Rgba32 OrangeRed = NamedColors<Rgba32>.OrangeRed;
public static readonly Rgba32 OrangeRed = Color.OrangeRed;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DA70D6.
/// </summary>
public static readonly Rgba32 Orchid = NamedColors<Rgba32>.Orchid;
public static readonly Rgba32 Orchid = Color.Orchid;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #EEE8AA.
/// </summary>
public static readonly Rgba32 PaleGoldenrod = NamedColors<Rgba32>.PaleGoldenrod;
public static readonly Rgba32 PaleGoldenrod = Color.PaleGoldenrod;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #98FB98.
/// </summary>
public static readonly Rgba32 PaleGreen = NamedColors<Rgba32>.PaleGreen;
public static readonly Rgba32 PaleGreen = Color.PaleGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #AFEEEE.
/// </summary>
public static readonly Rgba32 PaleTurquoise = NamedColors<Rgba32>.PaleTurquoise;
public static readonly Rgba32 PaleTurquoise = Color.PaleTurquoise;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DB7093.
/// </summary>
public static readonly Rgba32 PaleVioletRed = NamedColors<Rgba32>.PaleVioletRed;
public static readonly Rgba32 PaleVioletRed = Color.PaleVioletRed;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFEFD5.
/// </summary>
public static readonly Rgba32 PapayaWhip = NamedColors<Rgba32>.PapayaWhip;
public static readonly Rgba32 PapayaWhip = Color.PapayaWhip;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFDAB9.
/// </summary>
public static readonly Rgba32 PeachPuff = NamedColors<Rgba32>.PeachPuff;
public static readonly Rgba32 PeachPuff = Color.PeachPuff;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #CD853F.
/// </summary>
public static readonly Rgba32 Peru = NamedColors<Rgba32>.Peru;
public static readonly Rgba32 Peru = Color.Peru;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFC0CB.
/// </summary>
public static readonly Rgba32 Pink = NamedColors<Rgba32>.Pink;
public static readonly Rgba32 Pink = Color.Pink;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #DDA0DD.
/// </summary>
public static readonly Rgba32 Plum = NamedColors<Rgba32>.Plum;
public static readonly Rgba32 Plum = Color.Plum;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #B0E0E6.
/// </summary>
public static readonly Rgba32 PowderBlue = NamedColors<Rgba32>.PowderBlue;
public static readonly Rgba32 PowderBlue = Color.PowderBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #800080.
/// </summary>
public static readonly Rgba32 Purple = NamedColors<Rgba32>.Purple;
public static readonly Rgba32 Purple = Color.Purple;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #663399.
/// </summary>
public static readonly Rgba32 RebeccaPurple = NamedColors<Rgba32>.RebeccaPurple;
public static readonly Rgba32 RebeccaPurple = Color.RebeccaPurple;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF0000.
/// </summary>
public static readonly Rgba32 Red = NamedColors<Rgba32>.Red;
public static readonly Rgba32 Red = Color.Red;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #BC8F8F.
/// </summary>
public static readonly Rgba32 RosyBrown = NamedColors<Rgba32>.RosyBrown;
public static readonly Rgba32 RosyBrown = Color.RosyBrown;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #4169E1.
/// </summary>
public static readonly Rgba32 RoyalBlue = NamedColors<Rgba32>.RoyalBlue;
public static readonly Rgba32 RoyalBlue = Color.RoyalBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #8B4513.
/// </summary>
public static readonly Rgba32 SaddleBrown = NamedColors<Rgba32>.SaddleBrown;
public static readonly Rgba32 SaddleBrown = Color.SaddleBrown;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FA8072.
/// </summary>
public static readonly Rgba32 Salmon = NamedColors<Rgba32>.Salmon;
public static readonly Rgba32 Salmon = Color.Salmon;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F4A460.
/// </summary>
public static readonly Rgba32 SandyBrown = NamedColors<Rgba32>.SandyBrown;
public static readonly Rgba32 SandyBrown = Color.SandyBrown;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #2E8B57.
/// </summary>
public static readonly Rgba32 SeaGreen = NamedColors<Rgba32>.SeaGreen;
public static readonly Rgba32 SeaGreen = Color.SeaGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFF5EE.
/// </summary>
public static readonly Rgba32 SeaShell = NamedColors<Rgba32>.SeaShell;
public static readonly Rgba32 SeaShell = Color.SeaShell;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #A0522D.
/// </summary>
public static readonly Rgba32 Sienna = NamedColors<Rgba32>.Sienna;
public static readonly Rgba32 Sienna = Color.Sienna;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #C0C0C0.
/// </summary>
public static readonly Rgba32 Silver = NamedColors<Rgba32>.Silver;
public static readonly Rgba32 Silver = Color.Silver;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #87CEEB.
/// </summary>
public static readonly Rgba32 SkyBlue = NamedColors<Rgba32>.SkyBlue;
public static readonly Rgba32 SkyBlue = Color.SkyBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #6A5ACD.
/// </summary>
public static readonly Rgba32 SlateBlue = NamedColors<Rgba32>.SlateBlue;
public static readonly Rgba32 SlateBlue = Color.SlateBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #708090.
/// </summary>
public static readonly Rgba32 SlateGray = NamedColors<Rgba32>.SlateGray;
public static readonly Rgba32 SlateGray = Color.SlateGray;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFAFA.
/// </summary>
public static readonly Rgba32 Snow = NamedColors<Rgba32>.Snow;
public static readonly Rgba32 Snow = Color.Snow;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #00FF7F.
/// </summary>
public static readonly Rgba32 SpringGreen = NamedColors<Rgba32>.SpringGreen;
public static readonly Rgba32 SpringGreen = Color.SpringGreen;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #4682B4.
/// </summary>
public static readonly Rgba32 SteelBlue = NamedColors<Rgba32>.SteelBlue;
public static readonly Rgba32 SteelBlue = Color.SteelBlue;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #D2B48C.
/// </summary>
public static readonly Rgba32 Tan = NamedColors<Rgba32>.Tan;
public static readonly Rgba32 Tan = Color.Tan;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #008080.
/// </summary>
public static readonly Rgba32 Teal = NamedColors<Rgba32>.Teal;
public static readonly Rgba32 Teal = Color.Teal;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #D8BFD8.
/// </summary>
public static readonly Rgba32 Thistle = NamedColors<Rgba32>.Thistle;
public static readonly Rgba32 Thistle = Color.Thistle;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FF6347.
/// </summary>
public static readonly Rgba32 Tomato = NamedColors<Rgba32>.Tomato;
public static readonly Rgba32 Tomato = Color.Tomato;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFFFF.
/// </summary>
public static readonly Rgba32 Transparent = NamedColors<Rgba32>.Transparent;
public static readonly Rgba32 Transparent = Color.Transparent;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #40E0D0.
/// </summary>
public static readonly Rgba32 Turquoise = NamedColors<Rgba32>.Turquoise;
public static readonly Rgba32 Turquoise = Color.Turquoise;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #EE82EE.
/// </summary>
public static readonly Rgba32 Violet = NamedColors<Rgba32>.Violet;
public static readonly Rgba32 Violet = Color.Violet;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F5DEB3.
/// </summary>
public static readonly Rgba32 Wheat = NamedColors<Rgba32>.Wheat;
public static readonly Rgba32 Wheat = Color.Wheat;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFFFF.
/// </summary>
public static readonly Rgba32 White = NamedColors<Rgba32>.White;
public static readonly Rgba32 White = Color.White;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #F5F5F5.
/// </summary>
public static readonly Rgba32 WhiteSmoke = NamedColors<Rgba32>.WhiteSmoke;
public static readonly Rgba32 WhiteSmoke = Color.WhiteSmoke;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #FFFF00.
/// </summary>
public static readonly Rgba32 Yellow = NamedColors<Rgba32>.Yellow;
public static readonly Rgba32 Yellow = Color.Yellow;
/// <summary>
/// Represents a <see cref="Rgba32"/> matching the W3C definition that has an hex value of #9ACD32.
/// </summary>
public static readonly Rgba32 YellowGreen = NamedColors<Rgba32>.YellowGreen;
public static readonly Rgba32 YellowGreen = Color.YellowGreen;
}
}

14
src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs

@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to mutate.</param>
/// <param name="operation">The operation to perform on the source.</param>
public static void Mutate<TPixel>(this Image<TPixel> source, Action<IImageProcessingContext<TPixel>> operation)
public static void Mutate<TPixel>(this Image<TPixel> source, Action<IImageProcessingContext> operation)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(operation, nameof(operation));
@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to mutate.</param>
/// <param name="operations">The operations to perform on the source.</param>
public static void Mutate<TPixel>(this Image<TPixel> source, params IImageProcessor<TPixel>[] operations)
public static void Mutate<TPixel>(this Image<TPixel> source, params IImageProcessor[] operations)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(operations, nameof(operations));
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image to clone.</param>
/// <param name="operation">The operation to perform on the clone.</param>
/// <returns>The new <see cref="SixLabors.ImageSharp.Image{TPixel}"/></returns>
public static Image<TPixel> Clone<TPixel>(this Image<TPixel> source, Action<IImageProcessingContext<TPixel>> operation)
public static Image<TPixel> Clone<TPixel>(this Image<TPixel> source, Action<IImageProcessingContext> operation)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(operation, nameof(operation));
@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Processing
/// <param name="source">The image to clone.</param>
/// <param name="operations">The operations to perform on the clone.</param>
/// <returns>The new <see cref="SixLabors.ImageSharp.Image{TPixel}"/></returns>
public static Image<TPixel> Clone<TPixel>(this Image<TPixel> source, params IImageProcessor<TPixel>[] operations)
public static Image<TPixel> Clone<TPixel>(this Image<TPixel> source, params IImageProcessor[] operations)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(operations, nameof(operations));
@ -122,14 +122,12 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Applies the given <see cref="IImageProcessor{TPixel}"/> collection against the context
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image processing context.</param>
/// <param name="operations">The operations to perform on the source.</param>
/// <returns>The <see cref="IImageProcessor{TPixel}"/> to allow chaining of operations.</returns>
public static IImageProcessingContext<TPixel> ApplyProcessors<TPixel>(this IImageProcessingContext<TPixel> source, params IImageProcessor<TPixel>[] operations)
where TPixel : struct, IPixel<TPixel>
public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations)
{
foreach (IImageProcessor<TPixel> p in operations)
foreach (IImageProcessor p in operations)
{
source = source.ApplyProcessor(p);
}

2
tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(System.Drawing.Color.HotPink, 10))
using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10))
{
graphics.DrawBeziers(pen, new[] {
new PointF(10, 500),

2
tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(System.Drawing.Color.HotPink, 10))
using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10))
{
graphics.DrawLines(pen, new[] {
new PointF(10, 10),

2
tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(System.Drawing.Color.HotPink, 10))
using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10))
{
graphics.DrawPolygon(pen, new[] {
new PointF(10, 10),

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

@ -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<Rgba32>(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(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))));
}
}
@ -55,8 +55,14 @@ namespace SixLabors.ImageSharp.Benchmarks
image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)));
}
IImageProcessingContext<TPixel> DrawTextOldVersion<TPixel>(IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush<TPixel> brush, IPen<TPixel> pen, SixLabors.Primitives.PointF location)
where TPixel : struct, IPixel<TPixel>
IImageProcessingContext DrawTextOldVersion(
IImageProcessingContext source,
TextGraphicsOptions options,
string text,
SixLabors.Fonts.Font font,
IBrush brush,
IPen pen,
SixLabors.Primitives.PointF location)
{
float dpiX = 72;
float dpiY = 72;

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

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var pen = new Pen(System.Drawing.Color.HotPink, 10))
using (var pen = new System.Drawing.Pen(System.Drawing.Color.HotPink, 10))
using (var font = new Font("Arial", 12, GraphicsUnit.Point))
using (var gp = new GraphicsPath())
{
@ -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<Rgba32>(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(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10))));
}
}
@ -53,11 +53,25 @@ 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, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)));
image.Mutate(
x => DrawTextOldVersion(
x,
new TextGraphicsOptions(true) { WrapTextWidth = 780 },
TextToRender,
font,
null,
Processing.Pens.Solid(Rgba32.HotPink, 10),
new SixLabors.Primitives.PointF(10, 10)));
}
IImageProcessingContext<TPixel> DrawTextOldVersion<TPixel>(IImageProcessingContext<TPixel> source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, IBrush<TPixel> brush, IPen<TPixel> pen, SixLabors.Primitives.PointF location)
where TPixel : struct, IPixel<TPixel>
IImageProcessingContext DrawTextOldVersion(
IImageProcessingContext source,
TextGraphicsOptions options,
string text,
SixLabors.Fonts.Font font,
IBrush brush,
IPen pen,
SixLabors.Primitives.PointF location)
{
var style = new SixLabors.Fonts.RendererOptions(font, options.DpiX, options.DpiY, location)
{

10
tests/ImageSharp.Benchmarks/Samplers/Resize.cs

@ -91,12 +91,12 @@ namespace SixLabors.ImageSharp.Benchmarks
}
}
protected abstract void ExecuteResizeOperation(IImageProcessingContext<TPixel> ctx);
protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx);
}
public class Resize_Bicubic_Rgba32 : ResizeBenchmarkBase<Rgba32>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
protected override void ExecuteResizeOperation(IImageProcessingContext ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Benchmarks
public class Resize_Bicubic_Bgra32 : ResizeBenchmarkBase<Bgra32>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Bgra32> ctx)
protected override void ExecuteResizeOperation(IImageProcessingContext ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Benchmarks
public class Resize_Bicubic_Rgb24 : ResizeBenchmarkBase<Rgb24>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgb24> ctx)
protected override void ExecuteResizeOperation(IImageProcessingContext ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp.Benchmarks
public class Resize_BicubicCompand_Rgba32 : ResizeBenchmarkBase<Rgba32>
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
protected override void ExecuteResizeOperation(IImageProcessingContext ctx)
{
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true);
}

11
tests/ImageSharp.Tests/Color/ColorTests.cs

@ -13,6 +13,17 @@ namespace SixLabors.ImageSharp.Tests
{
public partial class ColorTests
{
[Fact]
public void WithAlpha()
{
Color c1 = Color.FromRgba(111, 222, 55, 255);
Color c2 = c1.WithAlpha(0.5f);
Rgba32 expected = new Rgba32(111, 222, 55, 128);
Assert.Equal(expected, (Rgba32)c2);
}
[Fact]
public void Equality_WhenTrue()
{

92
tests/ImageSharp.Tests/Drawing/BeziersTests.cs

@ -1,92 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class Beziers : FileTestBase
{
[Fact]
public void ImageShouldBeOverlayedByBezierLine()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Color.Blue));
image.Mutate(
x => x.DrawBeziers(
Rgba32.HotPink,
5,
new SixLabors.Primitives.PointF[]
{
new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400)
}));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
//top of curve
Assert.Equal(Rgba32.HotPink, sourcePixels[138, 115]);
//start points
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 395]);
Assert.Equal(Rgba32.HotPink, sourcePixels[300, 395]);
//curve points should not be never be set
Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]);
}
}
[Fact]
public void ImageShouldBeOverlayedBezierLineWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine");
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Color.Blue));
image.Mutate(
x => x.DrawBeziers(
color,
10,
new SixLabors.Primitives.PointF[]
{
new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400)
}));
image.Save($"{path}/Opacity.png");
//shift background color towards foreground color by the opacity amount
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
// top of curve
Assert.Equal(mergedColor, sourcePixels[138, 115]);
// start points
Assert.Equal(mergedColor, sourcePixels[10, 395]);
Assert.Equal(mergedColor, sourcePixels[300, 395]);
// curve points should not be never be set
Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]);
// inside shape should be empty
Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]);
}
}
}
}

48
tests/ImageSharp.Tests/Drawing/DrawBezierTests.cs

@ -0,0 +1,48 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class DrawBezierTests
{
public static readonly TheoryData<string, byte, float> DrawPathData = new TheoryData<string, byte, float>
{
{ "White", 255, 1.5f },
{ "Red", 255, 3 },
{ "HotPink", 255, 5 },
{ "HotPink", 150, 5 },
{ "White", 255, 15 },
};
[Theory]
[WithSolidFilledImages(nameof(DrawPathData), 300, 450, "Blue", PixelTypes.Rgba32)]
public void DrawBeziers<TPixel>(TestImageProvider<TPixel> provider, string colorName, byte alpha, float thickness)
where TPixel : struct, IPixel<TPixel>
{
var points = new SixLabors.Primitives.PointF[]
{
new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400)
};
Rgba32 rgba = TestUtils.GetColorByName(colorName);
rgba.A = alpha;
Color color = rgba;
FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}";
provider.RunValidatingProcessorTest( x => x.DrawBeziers(color, 5f, points),
testDetails,
appendSourceFileOrDescription: false,
appendPixelTypeToFileName: false);
}
}
}

70
tests/ImageSharp.Tests/Drawing/DrawComplexPolygonTests.cs

@ -0,0 +1,70 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class DrawComplexPolygonTests
{
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, false, false)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, true, false, false)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, true, false)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, false, false, true)]
public void DrawComplexPolygon<TPixel>(TestImageProvider<TPixel> provider, bool overlap, bool transparent, bool dashed)
where TPixel :struct, IPixel<TPixel>
{
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
overlap ? new Vector2(130, 40) : new Vector2(93, 85),
new Vector2(65, 137)));
IPath clipped = simplePath.Clip(hole1);
Rgba32 colorRgba = Rgba32.White;
if (transparent)
{
colorRgba.A = 150;
}
Color color = colorRgba;
string testDetails = "";
if (overlap)
{
testDetails += "_Overlap";
}
if (transparent)
{
testDetails += "_Transparent";
}
if (dashed)
{
testDetails += "_Dashed";
}
Pen pen = dashed ? Pens.Dash(color, 5f) : Pens.Solid(color, 5f);
provider.RunValidatingProcessorTest(
x => x.Draw(pen, clipped),
testDetails,
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
}
}

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

@ -0,0 +1,100 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class DrawLinesTests
{
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 2.5, true)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, 10, true)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, 10, true)]
public void DrawLines_Simple<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias)
where TPixel : struct, IPixel<TPixel>
{
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)
where TPixel : struct, IPixel<TPixel>
{
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)
where TPixel : struct, IPixel<TPixel>
{
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)
where TPixel : struct, IPixel<TPixel>
{
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
Pen pen = Pens.DashDot(color, thickness);
DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen);
}
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Black", 1f, 5, false)]
public void DrawLines_DashDotDot<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias)
where TPixel : struct, IPixel<TPixel>
{
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,
float alpha,
float thickness,
bool antialias,
Pen pen)
where TPixel : struct, IPixel<TPixel>
{
SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) };
GraphicsOptions options = new GraphicsOptions(antialias);
string aa = antialias ? "" : "_NoAntialias";
FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}";
provider.RunValidatingProcessorTest(
c => c.DrawLines(options, pen, simplePath),
outputDetails,
appendSourceFileOrDescription: false);
}
}
}

133
tests/ImageSharp.Tests/Drawing/DrawPathTests.cs

@ -1,112 +1,79 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class DrawPathTests : FileTestBase
[GroupOutput("Drawing")]
public class DrawPathTests
{
[Fact]
public void ImageShouldBeOverlayedByPath()
public static readonly TheoryData<string, byte, float> DrawPathData = new TheoryData<string, byte, float>
{
{ "White", 255, 1.5f },
{ "Red", 255, 3 },
{ "HotPink", 255, 5 },
{ "HotPink", 150, 5 },
{ "White", 255, 15 },
};
[Theory]
[WithSolidFilledImages(nameof(DrawPathData), 300, 450, "Blue", PixelTypes.Rgba32)]
public void DrawPath<TPixel>(TestImageProvider<TPixel> provider, string colorName, byte alpha, float thickness)
where TPixel : struct, IPixel<TPixel>
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path");
using (var image = new Image<Rgba32>(500, 500))
{
var linerSegemnt = new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300));
var bazierSegment = new CubicBezierLineSegment(
new Vector2(50, 300),
new Vector2(500, 500),
new Vector2(60, 10),
new Vector2(10, 400));
var p = new Path(linerSegemnt, bazierSegment);
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(Rgba32.HotPink, 5, p));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
[Fact]
public void ImageShouldBeOverlayedPathWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path");
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
var linerSegemnt = new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
);
var bazierSegment = new CubicBezierLineSegment(new Vector2(50, 300),
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300));
var bazierSegment = new CubicBezierLineSegment(
new Vector2(50, 300),
new Vector2(500, 500),
new Vector2(60, 10),
new Vector2(10, 400));
var p = new Path(linerSegemnt, bazierSegment);
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(color, 10, p));
image.Save($"{path}/Opacity.png");
var path = new Path(linerSegemnt, bazierSegment);
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Rgba32 rgba = TestUtils.GetColorByName(colorName);
rgba.A = alpha;
Color color = rgba;
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[11, 11]);
FormattableString testDetails = $"{colorName}_A{alpha}_T{thickness}";
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
provider.RunValidatingProcessorTest(
x => x.Draw(color, thickness, path),
testDetails,
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
[Fact]
public void PathExtendingOffEdgeOfImageShouldNotBeCropped()
[Theory]
[WithSolidFilledImages(256, 256, "Black", PixelTypes.Rgba32)]
public void PathExtendingOffEdgeOfImageShouldNotBeCropped<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path");
using (var image = new Image<Rgba32>(256, 256))
{
image.Mutate(x => x.Fill(Rgba32.Black));
Pen<Rgba32> pen = Pens.Solid(Rgba32.White, 5f);
for (int i = 0; i < 300; i += 20)
{
image.Mutate(
x => x.DrawLines(
pen,
new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) }));
}
image.Save($"{path}/ClippedLines.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.White, sourcePixels[0, 90]);
}
Color color = Color.White;
Pen pen = Pens.Solid(color, 5f);
provider.RunValidatingProcessorTest(
x =>
{
for (int i = 0; i < 300; i += 20)
{
PointF[] points = new PointF[] { new Vector2(100, 2), new Vector2(-10, i) };
x.DrawLines(pen, points);
}
},
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
}
}

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

@ -0,0 +1,43 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class DrawPolygonTests
{
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 2.5, true)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, 10, true)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, 10, true)]
public void DrawPolygon<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, float thickness, bool antialias)
where TPixel : struct, IPixel<TPixel>
{
SixLabors.Primitives.PointF[] simplePath =
{
new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)
};
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
GraphicsOptions options = new GraphicsOptions(antialias);
string aa = antialias ? "" : "_NoAntialias";
FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}";
provider.RunValidatingProcessorTest(
c => c.DrawPolygon(options, color, thickness, simplePath),
outputDetails,
appendSourceFileOrDescription: false);
}
}
}

62
tests/ImageSharp.Tests/Drawing/FillComplexPolygonTests.cs

@ -0,0 +1,62 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class FillComplexPolygonTests
{
[Theory]
[WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, false, false)]
[WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, true, false)]
[WithSolidFilledImages(300, 400, "Blue", PixelTypes.Rgba32, false, true)]
public void ComplexPolygon_SolidFill<TPixel>(TestImageProvider<TPixel> provider, bool overlap, bool transparent)
where TPixel :struct, IPixel<TPixel>
{
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
overlap ? new Vector2(130, 40) : new Vector2(93, 85),
new Vector2(65, 137)));
IPath clipped = simplePath.Clip(hole1);
Rgba32 colorRgba = Rgba32.HotPink;
if (transparent)
{
colorRgba.A = 150;
}
Color color = colorRgba;
string testDetails = "";
if (overlap)
{
testDetails += "_Overlap";
}
if (transparent)
{
testDetails += "_Transparent";
}
provider.RunValidatingProcessorTest(
x => x.Fill(color, clipped),
testDetails,
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false);
}
}
}

36
tests/ImageSharp.Tests/Drawing/FillEllipticGradientBrushTest.cs

@ -23,18 +23,18 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
TPixel red = NamedColors<TPixel>.Red;
Color red = Color.Red;
using (Image<TPixel> image = provider.GetImage())
{
var unicolorLinearGradientBrush =
new EllipticGradientBrush<TPixel>(
new EllipticGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(10, 0),
1.0f,
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, red));
new ColorStop(0, red),
new ColorStop(1, red));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
@ -58,22 +58,22 @@ namespace SixLabors.ImageSharp.Tests.Drawing
float ratio)
where TPixel : struct, IPixel<TPixel>
{
TPixel yellow = NamedColors<TPixel>.Yellow;
TPixel red = NamedColors<TPixel>.Red;
TPixel black = NamedColors<TPixel>.Black;
Color yellow = Color.Yellow;
Color red = Color.Red;
Color black = Color.Black;
provider.VerifyOperation(
TolerantComparer,
image =>
{
var unicolorLinearGradientBrush = new EllipticGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new EllipticGradientBrush(
new SixLabors.Primitives.Point(image.Width / 2, image.Height / 2),
new SixLabors.Primitives.Point(image.Width / 2, (image.Width * 2) / 3),
ratio,
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, yellow),
new ColorStop<TPixel>(1, red),
new ColorStop<TPixel>(1, black));
new ColorStop(0, yellow),
new ColorStop(1, red),
new ColorStop(1, black));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
},
@ -114,9 +114,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TolerantComparer,
image =>
{
TPixel yellow = NamedColors<TPixel>.Yellow;
TPixel red = NamedColors<TPixel>.Red;
TPixel black = NamedColors<TPixel>.Black;
Color yellow = Color.Yellow;
Color red = Color.Red;
Color black = Color.Black;
var center = new SixLabors.Primitives.Point(image.Width / 2, image.Height / 2);
@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int axisX = center.X + (int)-(offsetY * sin);
int axisY = center.Y + (int)(offsetY * cos);
var unicolorLinearGradientBrush = new EllipticGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new EllipticGradientBrush(
center,
new SixLabors.Primitives.Point(axisX, axisY),
ratio,
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, yellow),
new ColorStop<TPixel>(1, red),
new ColorStop<TPixel>(1, black));
new ColorStop(0, yellow),
new ColorStop(1, red),
new ColorStop(1, black));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
},

21
tests/ImageSharp.Tests/Drawing/FillImageBrushTests.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using (var src = new Image<Rgba32>(5, 5))
{
var brush = new ImageBrush<Rgba32>(src);
var brush = new ImageBrush(src);
using (var dest = new Image<Rgba32>(10, 10))
{
dest.Mutate(c => c.Fill(brush, new Rectangle(0, 0, 10, 10)));
@ -32,5 +32,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}
}
}
[Theory]
[WithTestPatternImages(200, 200, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
public void UseBrushOfDifferentPixelType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
byte[] data = TestFile.Create(TestImages.Png.Ducky).Bytes;
using (Image<TPixel> background = provider.GetImage())
using (Image overlay = provider.PixelType == PixelTypes.Rgba32
? (Image)Image.Load<Bgra32>(data)
: Image.Load<Rgba32>(data))
{
var brush = new ImageBrush(overlay);
background.Mutate(c => c.Fill(brush));
background.DebugSave(provider, appendSourceFileOrDescription : false);
background.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false);
}
}
}
}

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

@ -29,14 +29,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using (Image<TPixel> image = provider.GetImage())
{
TPixel red = NamedColors<TPixel>.Red;
Color red = Color.Red;
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(10, 0),
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, red));
new ColorStop(0, red),
new ColorStop(1, red));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
@ -58,12 +58,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TolerantComparer,
image =>
{
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(image.Width, 0),
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, NamedColors<TPixel>.Blue),
new ColorStop<TPixel>(1, NamedColors<TPixel>.Yellow));
new ColorStop(0, Color.Blue),
new ColorStop(1, Color.Yellow));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
},
@ -79,15 +79,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TolerantComparer,
image =>
{
TPixel red = NamedColors<TPixel>.Red;
TPixel yellow = NamedColors<TPixel>.Yellow;
Color red = Color.Red;
Color yellow = Color.Yellow;
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(image.Width, 0),
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, yellow));
new ColorStop(0, red),
new ColorStop(1, yellow));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
},
@ -109,15 +109,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TolerantComparer,
image =>
{
TPixel red = NamedColors<TPixel>.Red;
TPixel yellow = NamedColors<TPixel>.Yellow;
Color red = Color.Red;
Color yellow = Color.Yellow;
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(image.Width / 10, 0),
repetitionMode,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, yellow));
new ColorStop(0, red),
new ColorStop(1, yellow));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
},
@ -140,26 +140,26 @@ namespace SixLabors.ImageSharp.Tests.Drawing
// ensure the input data is valid
Assert.True(pattern.Length > 0);
TPixel black = NamedColors<TPixel>.Black;
TPixel white = NamedColors<TPixel>.White;
Color black = Color.Black;
Color white = Color.White;
// create the input pattern: 0, followed by each of the arguments twice, followed by 1.0 - toggling black and white.
ColorStop<TPixel>[] colorStops =
Enumerable.Repeat(new ColorStop<TPixel>(0, black), 1)
ColorStop[] colorStops =
Enumerable.Repeat(new ColorStop(0, black), 1)
.Concat(
pattern
.SelectMany((f, index) => new[]
{
new ColorStop<TPixel>(f, index % 2 == 0 ? black : white),
new ColorStop<TPixel>(f, index % 2 == 0 ? white : black)
new ColorStop(f, index % 2 == 0 ? black : white),
new ColorStop(f, index % 2 == 0 ? white : black)
}))
.Concat(Enumerable.Repeat(new ColorStop<TPixel>(1, pattern.Length % 2 == 0 ? black : white), 1))
.Concat(Enumerable.Repeat(new ColorStop(1, pattern.Length % 2 == 0 ? black : white), 1))
.ToArray();
using (Image<TPixel> image = provider.GetImage())
{
var unicolorLinearGradientBrush =
new LinearGradientBrush<TPixel>(
new LinearGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(image.Width, 0),
GradientRepetitionMode.None,
@ -176,7 +176,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
// the result must be a black and white pattern, no other color should occur:
Assert.All(
Enumerable.Range(0, image.Width).Select(i => image[i, 0]),
color => Assert.True(color.Equals(black) || color.Equals(white)));
color => Assert.True(
color.Equals(black.ToPixel<TPixel>()) || color.Equals(white.ToPixel<TPixel>())));
image.CompareToReferenceOutput(
TolerantComparer,
@ -196,15 +197,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing
provider.VerifyOperation(
image =>
{
TPixel red = NamedColors<TPixel>.Red;
TPixel yellow = NamedColors<TPixel>.Yellow;
Color red = Color.Red;
Color yellow = Color.Yellow;
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(0, 0),
new SixLabors.Primitives.Point(0, image.Height),
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, yellow));
new ColorStop(0, red),
new ColorStop(1, yellow));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
@ -254,16 +255,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int endX = image.Height - startX - 1;
int endY = image.Width - startY - 1;
TPixel red = NamedColors<TPixel>.Red;
TPixel yellow = NamedColors<TPixel>.Yellow;
Color red = Color.Red;
Color yellow = Color.Yellow;
var unicolorLinearGradientBrush =
new LinearGradientBrush<TPixel>(
new LinearGradientBrush(
new SixLabors.Primitives.Point(startX, startY),
new SixLabors.Primitives.Point(endX, endY),
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, yellow));
new ColorStop(0, red),
new ColorStop(1, yellow));
image.Mutate(x => x.Fill(unicolorLinearGradientBrush));
image.DebugSave(
@ -310,22 +311,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int[] stopColorCodes)
where TPixel : struct, IPixel<TPixel>
{
TPixel[] colors =
Color[] colors =
{
NamedColors<TPixel>.Navy, NamedColors<TPixel>.LightGreen, NamedColors<TPixel>.Yellow,
NamedColors<TPixel>.Red
Color.Navy, Color.LightGreen, Color.Yellow,
Color.Red
};
var coloringVariant = new StringBuilder();
var colorStops = new ColorStop<TPixel>[stopPositions.Length];
var colorStops = new ColorStop[stopPositions.Length];
for (int i = 0; i < stopPositions.Length; i++)
{
TPixel color = colors[stopColorCodes[i % colors.Length]];
Color color = colors[stopColorCodes[i % colors.Length]];
float position = stopPositions[i];
colorStops[i] = new ColorStop<TPixel>(position, color);
Rgba32 rgba = default;
color.ToRgba32(ref rgba);
colorStops[i] = new ColorStop(position, color);
Rgba32 rgba = color;
coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position);
}
@ -334,7 +334,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
provider.VerifyOperation(
image =>
{
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(startX, startY),
new SixLabors.Primitives.Point(endX, endY),
GradientRepetitionMode.None,
@ -357,22 +357,21 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int[] stopColorCodes)
where TPixel : struct, IPixel<TPixel>
{
TPixel[] colors =
Color[] colors =
{
NamedColors<TPixel>.Black, NamedColors<TPixel>.Blue, NamedColors<TPixel>.Red,
NamedColors<TPixel>.White, NamedColors<TPixel>.Lime
Color.Black, Color.Blue, Color.Red,
Color.White, Color.Lime
};
var coloringVariant = new StringBuilder();
var colorStops = new ColorStop<TPixel>[stopPositions.Length];
var colorStops = new ColorStop[stopPositions.Length];
for (int i = 0; i < stopPositions.Length; i++)
{
TPixel color = colors[stopColorCodes[i % colors.Length]];
Color color = colors[stopColorCodes[i % colors.Length]];
float position = stopPositions[i];
colorStops[i] = new ColorStop<TPixel>(position, color);
Rgba32 rgba = default;
color.ToRgba32(ref rgba);
colorStops[i] = new ColorStop(position, color);
Rgba32 rgba = color;
coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position);
}
@ -381,7 +380,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
provider.VerifyOperation(
image =>
{
var unicolorLinearGradientBrush = new LinearGradientBrush<TPixel>(
var unicolorLinearGradientBrush = new LinearGradientBrush(
new SixLabors.Primitives.Point(startX, startY),
new SixLabors.Primitives.Point(endX, endY),
GradientRepetitionMode.None,

8
tests/ImageSharp.Tests/Drawing/FillPatternTests.cs → tests/ImageSharp.Tests/Drawing/FillPatternBrushTests.cs

@ -12,11 +12,11 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class FillPatternBrushTests : FileTestBase
public class FillPatternBrushTests
{
private void Test(string name, Rgba32 background, IBrush<Rgba32> brush, Rgba32[,] expectedPattern)
private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern)
{
string path = TestEnvironment.CreateOutputDirectory("Fill", "PatternBrush");
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FillPatternBrushTests");
using (var image = new Image<Rgba32>(20, 20))
{
image.Mutate(x => x.Fill(background).Fill(brush));
@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}
}
image.Mutate(x => x.Resize(80, 80));
image.Mutate(x => x.Resize(80, 80, KnownResamplers.NearestNeighbor));
image.Save($"{path}/{name}x4.png");
}
}

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

@ -0,0 +1,155 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class FillPolygonTests
{
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, true)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 0.6f, true)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, false)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Bgr24, "Yellow", 1f, true)]
public void FillPolygon_Solid<TPixel>(TestImageProvider<TPixel> provider, string colorName, float alpha, bool antialias)
where TPixel : struct, IPixel<TPixel>
{
SixLabors.Primitives.PointF[] simplePath =
{
new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)
};
Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha);
GraphicsOptions options = new GraphicsOptions(antialias);
string aa = antialias ? "" : "_NoAntialias";
FormattableString outputDetails = $"{colorName}_A{alpha}{aa}";
provider.RunValidatingProcessorTest(
c => c.FillPolygon(options, color, simplePath),
outputDetails,
appendSourceFileOrDescription: false);
}
[Theory]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)]
public void FillPolygon_Concave<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
var points = new SixLabors.Primitives.PointF[]
{
new Vector2(8, 8),
new Vector2(64, 8),
new Vector2(64, 64),
new Vector2(120, 64),
new Vector2(120, 120),
new Vector2(8, 120)
};
Color color = Color.LightGreen;
provider.RunValidatingProcessorTest(
c => c.FillPolygon(color, points),
appendSourceFileOrDescription: false,
appendPixelTypeToFileName: false);
}
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)]
public void FillPolygon_Pattern<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
SixLabors.Primitives.PointF[] simplePath =
{
new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)
};
Color color = Color.Yellow;
var brush = Brushes.Horizontal(color);
provider.RunValidatingProcessorTest(
c => c.FillPolygon(brush, simplePath),
appendSourceFileOrDescription: false);
}
[Theory]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Png.Ducky)]
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, TestImages.Bmp.Car)]
public void FillPolygon_ImageBrush<TPixel>(TestImageProvider<TPixel> provider, string brushImageName)
where TPixel : struct, IPixel<TPixel>
{
SixLabors.Primitives.PointF[] simplePath =
{
new Vector2(10, 10), new Vector2(200, 50), new Vector2(50, 200)
};
using (Image<TPixel> brushImage = Image.Load<TPixel>(TestFile.Create(brushImageName).Bytes))
{
var brush = new ImageBrush(brushImage);
provider.RunValidatingProcessorTest(
c => c.FillPolygon(brush, simplePath),
System.IO.Path.GetFileNameWithoutExtension(brushImageName),
appendSourceFileOrDescription: false);
}
}
[Theory]
[WithBasicTestPatternImages(250, 250, PixelTypes.Rgba32)]
public void Fill_RectangularPolygon<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
var polygon = new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140);
Color color = Color.White;
provider.RunValidatingProcessorTest(
c => c.Fill(color, polygon),
appendSourceFileOrDescription: false);
}
[Theory]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 50, 0f)]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, 20f)]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 3, 60, -180f)]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 5, 70, 0f)]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32, 7, 80, -180f)]
public void Fill_RegularPolygon<TPixel>(TestImageProvider<TPixel> provider, int vertices, float radius, float angleDeg)
where TPixel : struct, IPixel<TPixel>
{
float angle = GeometryUtilities.DegreeToRadian(angleDeg);
var polygon = new RegularPolygon(100, 100, vertices, radius, angle);
Color color = Color.Yellow;
FormattableString testOutput = $"V({vertices})_R({radius})_Ang({angleDeg})";
provider.RunValidatingProcessorTest(
c => c.Fill(color, polygon),
testOutput,
appendSourceFileOrDescription: false,
appendPixelTypeToFileName: false);
}
[Theory]
[WithBasicTestPatternImages(200, 200, PixelTypes.Rgba32)]
public void Fill_EllipsePolygon<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
var polygon = new EllipsePolygon(100, 100, 80, 120);
Color color = Color.Azure;
provider.RunValidatingProcessorTest(
c => c.Fill(color, polygon),
appendSourceFileOrDescription: false,
appendPixelTypeToFileName: false);
}
}
}

14
tests/ImageSharp.Tests/Drawing/FillRadialGradientBrushTests.cs

@ -21,15 +21,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using (Image<TPixel> image = provider.GetImage())
{
TPixel red = NamedColors<TPixel>.Red;
Color red = Color.Red;
var unicolorRadialGradientBrush =
new RadialGradientBrush<TPixel>(
new RadialGradientBrush(
new SixLabors.Primitives.Point(0, 0),
100,
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, red),
new ColorStop<TPixel>(1, red));
new ColorStop(0, red),
new ColorStop(1, red));
image.Mutate(x => x.Fill(unicolorRadialGradientBrush));
@ -56,12 +56,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
TolerantComparer,
image =>
{
var brush = new RadialGradientBrush<TPixel>(
var brush = new RadialGradientBrush(
new SixLabors.Primitives.Point(centerX, centerY),
image.Width / 2f,
GradientRepetitionMode.None,
new ColorStop<TPixel>(0, NamedColors<TPixel>.Red),
new ColorStop<TPixel>(1, NamedColors<TPixel>.Yellow));
new ColorStop(0, Color.Red),
new ColorStop(1, Color.Yellow));
image.Mutate(x => x.Fill(brush));
},

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

@ -8,6 +8,7 @@ using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
@ -30,14 +31,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
var bounds = new Rectangle(0, 0, 1, 1);
var brush = new Mock<IBrush<Rgba32>>();
var brush = new Mock<IBrush>();
var region = new MockRegion2(bounds);
var options = new GraphicsOptions(antialias)
{
AntialiasSubpixelDepth = 1
};
var processor = new FillRegionProcessor<Rgba32>(brush.Object, region, options);
var processor = new FillRegionProcessor(brush.Object, region, options);
var img = new Image<Rgba32>(1, 1);
processor.Apply(img, bounds);
@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing
public void FillOffCanvas()
{
var bounds = new Rectangle(-100, -10, 10, 10);
var brush = new Mock<IBrush<Rgba32>>();
var brush = new Mock<IBrush>();
var options = new GraphicsOptions(true);
var processor = new FillRegionProcessor<Rgba32>(brush.Object, new MockRegion1(), options);
var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options);
var img = new Image<Rgba32>(10, 10);
processor.Apply(img, bounds);
}
@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (var img = new Image<Rgba32>(10, 10))
{
img.Mutate(x => x.DrawLines(new Pen<Rgba32>(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] {
img.Mutate(x => x.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] {
new Vector2(-10, 5),
new Vector2(20, 5),
}));

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

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using (Image<TPixel> image = provider.GetImage())
{
TPixel color = NamedColors<TPixel>.HotPink;
Color color = Color.HotPink;
image.Mutate(c => c.Fill(color));
image.DebugSave(provider, appendPixelTypeToFileName: false);
@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using (Image<TPixel> image = provider.GetImage())
{
TPixel color = NamedColors<TPixel>.HotPink;
Color color = Color.HotPink;
image.Mutate(c => c.Fill(color));
image.DebugSave(provider, appendSourceFileOrDescription: false);
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
using (Image<TPixel> image = provider.GetImage())
{
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>(newColorName);
Color color = TestUtils.GetColorByName(newColorName);
image.Mutate(c => c.Fill(color));
image.DebugSave(
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})";
var region = new RectangleF(x0, y0, w, h);
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>("Blue");
Color color = TestUtils.GetColorByName("Blue");
provider.RunValidatingProcessorTest(c => c.Fill(color, region), testDetails, ImageComparer.Exact);
}
@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})";
var region = new RectangleF(x0, y0, w, h);
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>("Blue");
Color color = TestUtils.GetColorByName("Blue");
provider.RunValidatingProcessorTestOnWrappedMemoryImage(
c => c.Fill(color, region),
@ -150,11 +150,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
float blendPercentage)
where TPixel : struct, IPixel<TPixel>
{
var vec = TestUtils.GetPixelOfNamedColor<RgbaVector>(newColorName).ToVector4();
vec.W = alpha;
TPixel fillColor = default;
fillColor.FromVector4(vec);
Color fillColor = TestUtils.GetColorByName(newColorName).WithAlpha(alpha);
using (Image<TPixel> image = provider.GetImage())
{
@ -169,11 +165,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
{
var region = new ShapeRegion(new RectangularPolygon(0, 0, 16, 16));
image.Mutate(c => c.Fill(options, new SolidBrush<TPixel>(fillColor), region));
image.Mutate(c => c.Fill(options, new SolidBrush(fillColor), region));
}
else
{
image.Mutate(c => c.Fill(options, new SolidBrush<TPixel>(fillColor)));
image.Mutate(c => c.Fill(options, new SolidBrush(fillColor)));
}
var testOutputDetails = new
@ -194,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(
blenderMode,
PixelAlphaCompositionMode.SrcOver);
TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage);
TPixel expectedPixel = blender.Blend(bgColor, fillColor.ToPixel<TPixel>(), blendPercentage);
image.ComparePixelBufferTo(expectedPixel);
}

201
tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs

@ -1,201 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class LineComplexPolygonTests : FileTestBase
{
[Fact]
public void ImageShouldBeOverlayedByPolygonOutline()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(93, 85),
new Vector2(65, 137)));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Assert.Equal(Rgba32.HotPink, sourcePixels[37, 85]);
Assert.Equal(Rgba32.HotPink, sourcePixels[93, 85]);
Assert.Equal(Rgba32.HotPink, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
[Fact]
public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(207, 25),
new Vector2(263, 25),
new Vector2(235, 57)));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Save($"{path}/SimpleVanishHole.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
//Assert.Equal(Color.HotPink, sourcePixels[37, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[93, 85]);
//Assert.Equal(Color.HotPink, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
[Fact]
public void ImageShouldBeOverlayedByPolygonOutlineOverlapping()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(130, 40),
new Vector2(65, 137)));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)));
image.Save($"{path}/SimpleOverlapping.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]);
Assert.Equal(Rgba32.Blue, sourcePixels[130, 41]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
[Fact]
public void ImageShouldBeOverlayedByPolygonOutlineDashed()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(93, 85),
new Vector2(65, 137)));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1)));
image.Save($"{path}/Dashed.png");
}
}
[Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(93, 85),
new Vector2(65, 137)));
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Draw(color, 5, simplePath.Clip(hole1)));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[10, 10]);
Assert.Equal(mergedColor, sourcePixels[200, 150]);
Assert.Equal(mergedColor, sourcePixels[50, 300]);
Assert.Equal(mergedColor, sourcePixels[37, 85]);
Assert.Equal(mergedColor, sourcePixels[93, 85]);
Assert.Equal(mergedColor, sourcePixels[65, 137]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]);
//inside shape
Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]);
}
}
}
}

193
tests/ImageSharp.Tests/Drawing/LineTests.cs

@ -1,193 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class LineTests : FileTestBase
{
[Fact]
public void ImageShouldBeOverlayedByPath()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.DrawLines(
Rgba32.HotPink,
5,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
[Fact]
public void ImageShouldBeOverlayedByPath_NoAntialias()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.DrawLines(
new GraphicsOptions(false),
Rgba32.HotPink,
5,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Simple_noantialias.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
[Fact]
public void ImageShouldBeOverlayedByPathDashed()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.DrawLines(Pens.Dash(Rgba32.HotPink, 5),
new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Save($"{path}/Dashed.png");
}
}
[Fact]
public void ImageShouldBeOverlayedByPathDotted()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.DrawLines(Pens.Dot(Rgba32.HotPink, 5),
new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Save($"{path}/Dot.png");
}
}
[Fact]
public void ImageShouldBeOverlayedByPathDashDot()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.DrawLines(Pens.DashDot(Rgba32.HotPink, 5),
new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Save($"{path}/DashDot.png");
}
}
[Fact]
public void ImageShouldBeOverlayedByPathDashDotDot()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
var image = new Image<Rgba32>(500, 500);
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5),
new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
}));
image.Save($"{path}/DashDotDot.png");
}
[Fact]
public void ImageShouldBeOverlayedPathWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
var image = new Image<Rgba32>(500, 500);
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.DrawLines(
color,
10,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor =
new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[11, 11]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
[Fact]
public void ImageShouldBeOverlayedByPathOutline()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines");
var image = new Image<Rgba32>(500, 500);
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.DrawLines(
Rgba32.HotPink,
10,
new Vector2(10, 10),
new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150)));
image.Save($"{path}/Rectangle.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
}
}
}

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

@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public class DrawPathCollection : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Rgba32 color = Rgba32.HotPink;
Pen<Rgba32> pen = Pens.Solid(Rgba32.HotPink, 1);
Color color = Color.HotPink;
Pen pen = Pens.Solid(Rgba32.HotPink, 1);
IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
new Vector2(10,10),
new Vector2(20,10),
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options);
@ -81,14 +81,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
ComplexPolygon polygon = Assert.IsType<ComplexPolygon>(region.Shape);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}
@ -100,14 +100,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options);
ShapePath region = Assert.IsType<ShapePath>(processor.Region);
ComplexPolygon polygon = Assert.IsType<ComplexPolygon>(region.Shape);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}

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

@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public class FillPath : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Rgba32 color = Rgba32.HotPink;
SolidBrush<Rgba32> brush = Brushes.Solid(Rgba32.HotPink);
Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
IPath path = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
new Vector2(10,10),
new Vector2(20,10),
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsBrushAndPath()
{
this.operations.Fill(this.brush, this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>();
var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsBrushPathOptions()
{
this.operations.Fill(this.noneDefault, this.brush, this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>();
var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options);
@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsColorAndPath()
{
this.operations.Fill(this.color, this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>();
var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
LinearLineSegment segments = Assert.IsType<LinearLineSegment>(polygon.LineSegments[0]);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsColorPathAndOptions()
{
this.operations.Fill(this.noneDefault, this.color, this.path);
var processor = this.Verify<FillRegionProcessor<Rgba32>>();
var processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options);
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
LinearLineSegment segments = Assert.IsType<LinearLineSegment>(polygon.LineSegments[0]);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}

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

@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public class FillPathCollection : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Rgba32 color = Rgba32.HotPink;
SolidBrush<Rgba32> brush = Brushes.Solid(Rgba32.HotPink);
Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] {
new Vector2(10,10),
new Vector2(20,10),
@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options);
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
LinearLineSegment segments = Assert.IsType<LinearLineSegment>(polygon.LineSegments[0]);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}
@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
for (int i = 0; i < 2; i++)
{
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>(i);
FillRegionProcessor processor = this.Verify<FillRegionProcessor>(i);
Assert.Equal(this.noneDefault, processor.Options);
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
LinearLineSegment segments = Assert.IsType<LinearLineSegment>(polygon.LineSegments[0]);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}

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

@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public class FillPolygon : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Rgba32 color = Rgba32.HotPink;
SolidBrush<Rgba32> brush = Brushes.Solid(Rgba32.HotPink);
Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
SixLabors.Primitives.PointF[] path = {
new Vector2(10,10),
new Vector2(20,10),
@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
{
this.operations.FillPolygon(this.brush, this.path);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsBrushPathAndOptions()
{
this.operations.FillPolygon(this.noneDefault, this.brush, this.path);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options);
@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsColorAndPath()
{
this.operations.FillPolygon(this.color, this.path);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
LinearLineSegment segemnt = Assert.IsType<LinearLineSegment>(polygon.LineSegments[0]);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsColorPathAndOptions()
{
this.operations.FillPolygon(this.noneDefault, this.color, this.path);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options);
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Polygon polygon = Assert.IsType<Polygon>(region.Shape);
LinearLineSegment segemnt = Assert.IsType<LinearLineSegment>(polygon.LineSegments[0]);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}

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

@ -12,15 +12,15 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public class FillRectangle : BaseImageOperationsExtensionTest
{
GraphicsOptions noneDefault = new GraphicsOptions();
Rgba32 color = Rgba32.HotPink;
SolidBrush<Rgba32> brush = Brushes.Solid(Rgba32.HotPink);
Color color = Color.HotPink;
SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76);
[Fact]
public void CorrectlySetsBrushAndRectangle()
{
this.operations.Fill(this.brush, this.rectangle);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsBrushRectangleAndOptions()
{
this.operations.Fill(this.noneDefault, this.brush, this.rectangle);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options);
@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsColorAndRectangle()
{
this.operations.Fill(this.color, this.rectangle);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(GraphicsOptions.Default, processor.Options);
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Assert.Equal(rect.Size.Width, this.rectangle.Width);
Assert.Equal(rect.Size.Height, this.rectangle.Height);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
public void CorrectlySetsColorRectangleAndOptions()
{
this.operations.Fill(this.noneDefault, this.color, this.rectangle);
FillRegionProcessor<Rgba32> processor = this.Verify<FillRegionProcessor<Rgba32>>();
FillRegionProcessor processor = this.Verify<FillRegionProcessor>();
Assert.Equal(this.noneDefault, processor.Options);
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths
Assert.Equal(rect.Size.Width, this.rectangle.Width);
Assert.Equal(rect.Size.Height, this.rectangle.Height);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(this.color, brush.Color);
}
}

102
tests/ImageSharp.Tests/Drawing/PolygonTests.cs

@ -1,102 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.ImageSharp.Processing;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class PolygonTests : FileTestBase
{
[Fact]
public void ImageShouldBeOverlayedByPolygonOutline()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons");
using (Image<Rgba32> image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.DrawPolygon(
Rgba32.HotPink,
5,
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]);
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
[Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons");
PointF[] simplePath = {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
};
Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
using (Image<Rgba32> image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.DrawPolygon(color, 10, simplePath));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
Rgba32 mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[9, 9]);
Assert.Equal(mergedColor, sourcePixels[199, 149]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
[Fact]
public void ImageShouldBeOverlayedByRectangleOutline()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons");
using (Image<Rgba32> image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140)));
image.Save($"{path}/Rectangle.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
}
}

48
tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs

@ -1,48 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
public class RecolorImageTest : FileTestBase
{
[Fact]
public void ImageShouldRecolorYellowToHotPink()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage");
var brush = new RecolorBrush<Rgba32>(Rgba32.Yellow, Rgba32.HotPink, 0.2f);
foreach (TestFile file in Files)
{
using (Image<Rgba32> image = file.CreateRgba32Image())
{
image.Mutate(x => x.Fill(brush));
image.Save($"{path}/{file.FileName}");
}
}
}
[Fact]
public void ImageShouldRecolorYellowToHotPinkInARectangle()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage");
var brush = new RecolorBrush<Rgba32>(Rgba32.Yellow, Rgba32.HotPink, 0.2f);
foreach (TestFile file in Files)
{
using (Image<Rgba32> image = file.CreateRgba32Image())
{
int imageHeight = image.Height;
image.Mutate(x => x.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2)));
image.Save($"{path}/Shaped_{file.FileName}");
}
}
}
}
}

52
tests/ImageSharp.Tests/Drawing/RecolorImageTests.cs

@ -0,0 +1,52 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
[GroupOutput("Drawing")]
public class RecolorImageTests
{
[Theory]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, "Yellow", "Pink", 0.2f)]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgra32, "Yellow", "Pink", 0.5f)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.2f)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.6f)]
public void Recolor<TPixel>(TestImageProvider<TPixel> provider, string sourceColorName, string targetColorName, float threshold)
where TPixel : struct, IPixel<TPixel>
{
Color sourceColor = TestUtils.GetColorByName(sourceColorName);
Color targetColor = TestUtils.GetColorByName(targetColorName);
var brush = new RecolorBrush(sourceColor, targetColor, threshold);
FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}";
provider.RunValidatingProcessorTest(x => x.Fill(brush), testInfo);
}
[Theory]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgra32, "Yellow", "Pink", 0.5f)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, "Red", "Blue", 0.2f)]
public void Recolor_InBox<TPixel>(TestImageProvider<TPixel> provider, string sourceColorName, string targetColorName, float threshold)
where TPixel : struct, IPixel<TPixel>
{
Color sourceColor = TestUtils.GetColorByName(sourceColorName);
Color targetColor = TestUtils.GetColorByName(targetColorName);
var brush = new RecolorBrush(sourceColor, targetColor, threshold);
FormattableString testInfo = $"{sourceColorName}-{targetColorName}-{threshold}";
provider.RunValidatingProcessorTest(x =>
{
Size size = x.GetCurrentSize();
var rectangle = new Rectangle(0, size.Height / 2 - size.Height / 4, size.Width, size.Height / 2);
x.Fill(brush, rectangle);
}, testInfo);
}
}
}

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

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.BackgroundColor(blue));
image.Mutate(x => x.Fill(hotPink.ToPixel<TPixel>(), new Polygon(new CubicBezierLineSegment(simplePath))));
image.Mutate(x => x.Fill(hotPink, new Polygon(new CubicBezierLineSegment(simplePath))));
image.DebugSave(provider);
image.CompareToReferenceOutput(provider);
}

109
tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs

@ -1,109 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class SolidComplexPolygonTests : FileTestBase
{
[Fact]
public void ImageShouldBeOverlayedByPolygonOutline()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(93, 85),
new Vector2(65, 137)));
IPath clipped = simplePath.Clip(hole1);
// var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Fill(Rgba32.HotPink, clipped));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
}
[Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOverlap()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(130, 40),
new Vector2(65, 137)));
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Fill(Rgba32.HotPink, simplePath.Clip(hole1)));
image.Save($"{path}/SimpleOverlapping.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
}
[Fact]
public void ImageShouldBeOverlayedPolygonOutlineWithOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon");
var simplePath = new Polygon(new LinearLineSegment(
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)));
var hole1 = new Polygon(new LinearLineSegment(
new Vector2(37, 85),
new Vector2(93, 85),
new Vector2(65, 137)));
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Fill(color, simplePath.Clip(hole1)));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(mergedColor, sourcePixels[20, 35]);
//inside hole
Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]);
}
}
}
}

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

@ -43,11 +43,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int scaleY = img.Height / 100;
img.Mutate(
x => x.Fill(
NamedColors<TPixel>.DarkBlue,
Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)
)
.Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition },
NamedColors<TPixel>.HotPink,
Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))
);
@ -69,17 +69,17 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int scaleY = img.Height / 100;
img.Mutate(
x => x.Fill(
NamedColors<TPixel>.DarkBlue,
Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
NamedColors<TPixel>.HotPink,
Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
NamedColors<TPixel>.Transparent,
Color.Transparent,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
);
@ -101,22 +101,20 @@ namespace SixLabors.ImageSharp.Tests.Drawing
int scaleY = (img.Height / 100);
img.Mutate(
x => x.Fill(
NamedColors<TPixel>.DarkBlue,
Color.DarkBlue,
new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY)));
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
NamedColors<TPixel>.HotPink,
Color.HotPink,
new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY)));
var c = NamedColors<TPixel>.Red.ToVector4();
c.W *= 0.5f;
var pixel = default(TPixel);
pixel.FromVector4(c);
var transparentRed = Color.Red.WithAlpha(0.5f);
img.Mutate(
x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
pixel,
transparentRed,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
);
@ -139,12 +137,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing
dstImg.Mutate(
x => x.Fill(
NamedColors<TPixel>.DarkBlue,
Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
srcImg.Mutate(
x => x.Fill(
NamedColors<TPixel>.Black,
Color.Black,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
dstImg.Mutate(

240
tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs

@ -1,240 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Drawing
{
public class SolidPolygonTests : FileTestBase
{
[Fact]
public void ImageShouldBeOverlayedByFilledPolygon()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
SixLabors.Primitives.PointF[] simplePath = {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
};
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.FillPolygon(new GraphicsOptions(true), Rgba32.HotPink, simplePath));
image.Save($"{path}/Simple.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]);
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledPolygonWithPattern()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
};
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(
x => x.FillPolygon(new GraphicsOptions(true), Brushes.Horizontal(Rgba32.HotPink), simplePath));
image.Save($"{path}/Pattern.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]);
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledPolygonNoAntialias()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
};
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.FillPolygon(
new GraphicsOptions(false),
Rgba32.HotPink,
simplePath));
image.Save($"{path}/Simple_NoAntialias.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong");
Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong");
Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong");
Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong");
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledPolygonImage()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
};
using (Image<Rgba32> brushImage = TestFile.Create(TestImages.Bmp.Car).CreateRgba32Image())
using (var image = new Image<Rgba32>(500, 500))
{
var brush = new ImageBrush<Rgba32>(brushImage);
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.FillPolygon(brush, simplePath));
image.Save($"{path}/Image.png");
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledPolygonOpacity()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var simplePath = new SixLabors.Primitives.PointF[] {
new Vector2(10, 10),
new Vector2(200, 150),
new Vector2(50, 300)
};
var color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150);
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.FillPolygon(color, simplePath));
image.Save($"{path}/Opacity.png");
//shift background color towards forground color by the opacity amount
var mergedColor = new Rgba32(
Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f));
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledRectangle()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
using (var image = new Image<Rgba32>(500, 500))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.Fill(
Rgba32.HotPink,
new SixLabors.Shapes.RectangularPolygon(10, 10, 190, 140)));
image.Save($"{path}/Rectangle.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]);
Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]);
Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]);
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]);
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledTriangle()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
using (var image = new Image<Rgba32>(100, 100))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(
x => x.Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30)));
image.Save($"{path}/Triangle.png");
Buffer2D<Rgba32> sourcePixels = image.GetRootFramePixelBuffer();
Assert.Equal(Rgba32.Blue, sourcePixels[30, 65]);
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]);
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledSeptagon()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var config = Configuration.CreateDefaultInstance();
config.MaxDegreeOfParallelism = 1;
using (var image = new Image<Rgba32>(config, 100, 100))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x.Fill(Rgba32.HotPink,
new RegularPolygon(50, 50, 7, 30, -(float)Math.PI)));
image.Save($"{path}/Septagon.png");
}
}
[Fact]
public void ImageShouldBeOverlayedByFilledEllipse()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var config = Configuration.CreateDefaultInstance();
config.MaxDegreeOfParallelism = 1;
using (var image = new Image<Rgba32>(config, 100, 100))
{
image.Mutate(x => x.BackgroundColor(Rgba32.Blue));
image.Mutate(x => x
.Fill(Rgba32.HotPink, new EllipsePolygon(50, 50, 30, 50)
.Rotate((float)(Math.PI / 3))));
image.Save($"{path}/ellipse.png");
}
}
[Fact]
public void ImageShouldBeOverlayedBySquareWithCornerClipped()
{
string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons");
var config = Configuration.CreateDefaultInstance();
config.MaxDegreeOfParallelism = 1;
using (var image = new Image<Rgba32>(config, 200, 200))
{
image.Mutate(x => x
.Fill(Rgba32.Blue)
.FillPolygon(Rgba32.HotPink, new SixLabors.Primitives.PointF[]
{
new Vector2( 8, 8 ),
new Vector2( 64, 8 ),
new Vector2( 64, 64 ),
new Vector2( 120, 64 ),
new Vector2( 120, 120 ),
new Vector2( 8, 120 )
}));
image.Save($"{path}/clipped-corner.png");
}
}
}
}

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

@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
public class DrawText : BaseImageOperationsExtensionTest
{
Rgba32 color = Rgba32.HotPink;
Rgba32 color = Color.HotPink;
SolidBrush<Rgba32> brush = Brushes.Solid(Rgba32.HotPink);
SolidBrush brush = Brushes.Solid(Color.HotPink);
IPath path = new SixLabors.Shapes.Path(
new LinearLineSegment(
@ -39,57 +39,57 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
new TextGraphicsOptions(true),
"123",
this.Font,
Brushes.Solid(Rgba32.Red),
Brushes.Solid(Color.Red),
null,
Vector2.Zero);
this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions()
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero);
this.operations.DrawText("123", this.Font, Brushes.Solid(Color.Red), null, Vector2.Zero);
this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void FillsForEachACharachterWhenBrushSet()
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero);
this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void FillsForEachACharachterWhenBrushSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
this.operations.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero);
this.Verify<DrawTextProcessor<Rgba32>>(0);
this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void FillsForEachACharachterWhenColorSet()
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, Vector2.Zero);
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Color.Red, Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(Color.Red, brush.Color);
}
[Fact]
public void FillsForEachACharachterWhenColorSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero);
this.operations.DrawText("123", this.Font, Color.Red, Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
SolidBrush<Rgba32> brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
SolidBrush brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(Color.Red, brush.Color);
}
[Fact]
@ -100,39 +100,39 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
"123",
this.Font,
null,
Pens.Dash(Rgba32.Red, 1),
Pens.Dash(Color.Red, 1),
Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions()
{
this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
this.operations.DrawText("123", this.Font, null, Pens.Dash(Color.Red, 1), Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void DrawForEachACharachterWhenPenSet()
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
}
[Fact]
public void DrawForEachACharachterWhenPenSetDefaultOptions()
{
this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
this.operations.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
Assert.Equal("123", processor.Text);
Assert.Equal(this.Font, processor.Font);
var penBrush = Assert.IsType<SolidBrush<Rgba32>>(processor.Pen.StrokeFill);
Assert.Equal(Rgba32.Red, penBrush.Color);
var penBrush = Assert.IsType<SolidBrush>(processor.Pen.StrokeFill);
Assert.Equal(Color.Red, penBrush.Color);
Assert.Equal(1, processor.Pen.StrokeWidth);
Assert.Equal(PointF.Empty, processor.Location);
}
@ -144,19 +144,19 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
new TextGraphicsOptions(true),
"123",
this.Font,
Brushes.Solid(Rgba32.Red),
Pens.Dash(Rgba32.Red, 1),
Brushes.Solid(Color.Red),
Pens.Dash(Color.Red, 1),
Vector2.Zero);
var processor = this.Verify<DrawTextProcessor<Rgba32>>(0);
var processor = this.Verify<DrawTextProcessor>(0);
Assert.Equal("123", processor.Text);
Assert.Equal(this.Font, processor.Font);
var brush = Assert.IsType<SolidBrush<Rgba32>>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
var brush = Assert.IsType<SolidBrush>(processor.Brush);
Assert.Equal(Color.Red, brush.Color);
Assert.Equal(PointF.Empty, processor.Location);
var penBrush = Assert.IsType<SolidBrush<Rgba32>>(processor.Pen.StrokeFill);
Assert.Equal(Rgba32.Red, penBrush.Color);
var penBrush = Assert.IsType<SolidBrush>(processor.Pen.StrokeFill);
Assert.Equal(Color.Red, penBrush.Color);
Assert.Equal(1, processor.Pen.StrokeWidth);
}
}

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

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont("OpenSans-Regular.ttf", 36);
TPixel color = NamedColors<TPixel>.Black;
Color color = Color.Black;
float padding = 5;
var text = "A short piece of text";
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont(fontName, fontSize);
TPixel color = NamedColors<TPixel>.Black;
Color color = Color.Black;
provider.VerifyOperation(
TextDrawingComparer,
@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
HorizontalAlignment = HorizontalAlignment.Left,
};
TPixel color = NamedColors<TPixel>.Black;
Color color = Color.Black;
// Based on the reported 0.0270% difference with AccuracyMultiple = 8
// We should avoid quality regressions leading to higher difference!
@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont(fontName, fontSize);
TPixel color = NamedColors<TPixel>.Black;
Color color = Color.Black;
provider.VerifyOperation(
OutlinedTextDrawingComparer,
@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
where TPixel : struct, IPixel<TPixel>
{
Font font = CreateFont(fontName, fontSize);
TPixel color = NamedColors<TPixel>.Black;
Color color = Color.Black;
provider.VerifyOperation(
OutlinedTextDrawingComparer,
@ -217,7 +217,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
var comparer = ImageComparer.TolerantPercentage(0.2f);
provider.RunValidatingProcessorTest(
x => x.DrawText(textOptions, text, font, NamedColors<TPixel>.Black, new PointF(10, 50)),
x => x.DrawText(textOptions, text, font, Color.Black, new PointF(10, 50)),
details,
comparer,
appendPixelTypeToFileName: false,

49
tests/ImageSharp.Tests/ImageOperationTests.cs

@ -21,14 +21,15 @@ namespace SixLabors.ImageSharp.Tests
{
private readonly Image<Rgba32> image;
private readonly FakeImageOperationsProvider provider;
private readonly IImageProcessor<Rgba32> processor;
public Configuration Configuration { get; private set; }
private readonly IImageProcessor processorDefinition;
public ImageOperationTests()
{
this.provider = new FakeImageOperationsProvider();
this.processor = new Mock<IImageProcessor<Rgba32>>().Object;
Mock<IImageProcessor> processorMock = new Mock<IImageProcessor>();
this.processorDefinition = processorMock.Object;
this.image = new Image<Rgba32>(new Configuration()
{
ImageOperationsProvider = this.provider
@ -38,61 +39,73 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void MutateCallsImageOperationsProvider_Func_OriginalImage()
{
this.image.Mutate(x => x.ApplyProcessor(this.processor));
this.image.Mutate(x => x.ApplyProcessor(this.processorDefinition));
Assert.True(this.provider.HasCreated(this.image));
Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor));
Assert.Contains(
this.processorDefinition,
this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor));
}
[Fact]
public void MutateCallsImageOperationsProvider_ListOfProcessors_OriginalImage()
{
this.image.Mutate(this.processor);
this.image.Mutate(this.processorDefinition);
Assert.True(this.provider.HasCreated(this.image));
Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor));
Assert.Contains(
this.processorDefinition,
this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor));
}
[Fact]
public void CloneCallsImageOperationsProvider_Func_WithDuplicateImage()
{
Image<Rgba32> returned = this.image.Clone(x => x.ApplyProcessor(this.processor));
Image<Rgba32> returned = this.image.Clone(x => x.ApplyProcessor(this.processorDefinition));
Assert.True(this.provider.HasCreated(returned));
Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor));
Assert.Contains(
this.processorDefinition,
this.provider.AppliedOperations(returned).Select(x => x.NonGenericProcessor));
}
[Fact]
public void CloneCallsImageOperationsProvider_ListOfProcessors_WithDuplicateImage()
{
Image<Rgba32> returned = this.image.Clone(this.processor);
Image<Rgba32> returned = this.image.Clone(this.processorDefinition);
Assert.True(this.provider.HasCreated(returned));
Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor));
Assert.Contains(
this.processorDefinition,
this.provider.AppliedOperations(returned).Select(x => x.NonGenericProcessor));
}
[Fact]
public void CloneCallsImageOperationsProvider_Func_NotOnOrigional()
{
Image<Rgba32> returned = this.image.Clone(x => x.ApplyProcessor(this.processor));
Image<Rgba32> returned = this.image.Clone(x => x.ApplyProcessor(this.processorDefinition));
Assert.False(this.provider.HasCreated(this.image));
Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor));
Assert.DoesNotContain(
this.processorDefinition,
this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor));
}
[Fact]
public void CloneCallsImageOperationsProvider_ListOfProcessors_NotOnOrigional()
{
Image<Rgba32> returned = this.image.Clone(this.processor);
Image<Rgba32> returned = this.image.Clone(this.processorDefinition);
Assert.False(this.provider.HasCreated(this.image));
Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor));
Assert.DoesNotContain(
this.processorDefinition,
this.provider.AppliedOperations(this.image).Select(x => x.NonGenericProcessor));
}
[Fact]
public void ApplyProcessors_ListOfProcessors_AppliesAllProcessorsToOperation()
{
var operations = new FakeImageOperationsProvider.FakeImageOperations<Rgba32>(null, false);
operations.ApplyProcessors(this.processor);
Assert.Contains(this.processor, operations.Applied.Select(x => x.GenericProcessor));
operations.ApplyProcessors(this.processorDefinition);
Assert.Contains(this.processorDefinition, operations.Applied.Select(x => x.NonGenericProcessor));
}
public void Dispose() => this.image.Dispose();

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

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Issues
{
context.DrawLines(
new GraphicsOptions(false),
NamedColors<TPixel>.Black,
Color.Black,
1,
new[]
{
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Issues
context.DrawLines(
new GraphicsOptions(false),
NamedColors<TPixel>.Red,
Color.Red,
1,
new[]
{
@ -46,8 +46,9 @@ namespace SixLabors.ImageSharp.Tests.Issues
{
for (var x = 0; x < 40; x++)
{
TPixel red = Color.Red.ToPixel<TPixel>();
Assert.True(NamedColors<TPixel>.Red.Equals(image[x, y]), $"expected {NamedColors<TPixel>.Red} but found {image[x, y]} at [{x}, {y}]");
Assert.True(red.Equals(image[x, y]), $"expected {Color.Red} but found {image[x, y]} at [{x}, {y}]");
}
}
}

19
tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs

@ -12,7 +12,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
{
public class BinaryDitherTests : FileTestBase
public class BinaryDitherTests
{
public static readonly string[] CommonTestImages =
{
@ -40,14 +40,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
{ "Stucki", KnownDiffusers.Stucki },
};
public const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24;
private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4;
private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson;
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), DefaultPixelType)]
[WithTestPatternImages(nameof(OrderedDitherers), 100, 100, DefaultPixelType)]
[WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), PixelTypes.Rgba32)]
[WithTestPatternImages(nameof(OrderedDitherers), 100, 100, PixelTypes.Rgba32)]
public void BinaryDitherFilter_WorksWithAllDitherers<TPixel>(TestImageProvider<TPixel> provider, string name, IOrderedDither ditherer)
where TPixel : struct, IPixel<TPixel>
{
@ -59,8 +60,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), DefaultPixelType)]
[WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, DefaultPixelType)]
[WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), PixelTypes.Rgba32)]
[WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, PixelTypes.Rgba32)]
public void DiffusionFilter_WorksWithAllErrorDiffusers<TPixel>(TestImageProvider<TPixel> provider, string name, IErrorDiffuser diffuser)
where TPixel : struct, IPixel<TPixel>
{
@ -72,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
}
[Theory]
[WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)]
[WithFile(TestImages.Png.Bike, TestPixelTypes)]
public void BinaryDitherFilter_ShouldNotDependOnSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -84,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
}
[Theory]
[WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)]
[WithFile(TestImages.Png.Bike, TestPixelTypes)]
public void DiffusionFilter_ShouldNotDependOnSinglePixelType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
}
[Theory]
[WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)]
public void ApplyDitherFilterInBox<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
}
[Theory]
[WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)]
public void ApplyDiffusionFilterInBox<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{

15
tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs

@ -11,17 +11,24 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
{
using SixLabors.ImageSharp.Processing;
public class BinaryThresholdTest : FileTestBase
public class BinaryThresholdTest
{
public static readonly TheoryData<float> BinaryThresholdValues
= new TheoryData<float>
= new TheoryData<float>
{
.25F,
.75F
};
public static readonly string[] CommonTestImages =
{
TestImages.Png.CalliphoraPartial, TestImages.Png.Bike
};
public const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24;
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)]
[WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)]
public void ImageShouldApplyBinaryThresholdFilter<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
@ -33,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
}
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)]
[WithFileCollection(nameof(CommonTestImages), nameof(BinaryThresholdValues), PixelTypes.Rgba32)]
public void ImageShouldApplyBinaryThresholdInBox<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{

6
tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs

@ -47,10 +47,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
ValidatorComparer);
}
protected abstract void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value)
where TPixel : struct, IPixel<TPixel>;
protected abstract void Apply(IImageProcessingContext ctx, int value);
protected abstract void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value, Rectangle bounds)
where TPixel : struct, IPixel<TPixel>;
protected abstract void Apply(IImageProcessingContext ctx, int value, Rectangle bounds);
}
}

4
tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs

@ -9,9 +9,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
[GroupOutput("Convolution")]
public class BoxBlurTest : Basic1ParameterConvolutionTests
{
protected override void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value) => ctx.BoxBlur(value);
protected override void Apply(IImageProcessingContext ctx, int value) => ctx.BoxBlur(value);
protected override void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value, Rectangle bounds) =>
protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) =>
ctx.BoxBlur(value, bounds);
}
}

4
tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs

@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
[GroupOutput("Convolution")]
public class GaussianBlurTest : Basic1ParameterConvolutionTests
{
protected override void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value) => ctx.GaussianBlur(value);
protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value);
protected override void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value, Rectangle bounds) =>
protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) =>
ctx.GaussianBlur(value, bounds);
}
}

4
tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs

@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
[GroupOutput("Convolution")]
public class GaussianSharpenTest : Basic1ParameterConvolutionTests
{
protected override void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value) => ctx.GaussianSharpen(value);
protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value);
protected override void Apply<TPixel>(IImageProcessingContext<TPixel> ctx, int value, Rectangle bounds) =>
protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) =>
ctx.GaussianSharpen(value, bounds);
}
}

3
tests/ImageSharp.Tests/Processing/Processors/Overlays/OverlayTestBase.cs

@ -27,8 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays
where TPixel : struct, IPixel<TPixel>
{
provider.Utility.TestGroupName = this.GetType().Name;
var f = (FieldInfo)typeof(Color).GetMember(colorName)[0];
Color color = (Color)f.GetValue(null);
Color color = TestUtils.GetColorByName(colorName);
provider.RunValidatingProcessorTest(x => this.Apply(x, color), colorName, ValidatorComparer, appendPixelTypeToFileName: false);
}

9
tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs

@ -7,10 +7,15 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
public class PadTest : FileTestBase
public class PadTest
{
public static readonly string[] CommonTestImages =
{
TestImages.Png.CalliphoraPartial, TestImages.Png.Bike
};
[Theory]
[WithFileCollection(nameof(DefaultFiles), DefaultPixelType)]
[WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)]
public void ImageShouldPad<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{

6
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -322,11 +322,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
FormattableString testOutputDetails = $"{samplerName}-{destSizeInfo}";
ctx.Apply(
img => img.DebugSave(
provider,
$"{testOutputDetails}-ORIGINAL",
appendPixelTypeToFileName: false));
ctx.Resize((Size)newSize, sampler, false);
return testOutputDetails;
},

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save