diff --git a/appveyor.yml b/appveyor.yml
index c456a8d722..89cb576539 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,9 @@
version: 1.0.0.{build}
image: Visual Studio 2017
+# prevent the double build when a branch has an active PR
+skip_branch_with_pr: true
+
init:
- ps: iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/8cf3160c9516ef1f4effc825c0a44acc918a0b5a/appveyor-build-info.ps'))
diff --git a/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs b/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs
index d77a6d5941..6e092bf185 100644
--- a/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs
+++ b/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs
@@ -18,10 +18,9 @@ namespace ImageSharp.Drawing.Brushes
///
/// Percent10 Hatch Pattern
///
- /// note 2d arrays when configured using initalizer look inverted
- /// ---> Y axis
+ /// ---> x axis
/// ^
- /// | X - axis
+ /// | y - axis
/// |
/// see PatternBrush for details about how to make new patterns work
private static readonly bool[,] Percent10Pattern =
@@ -37,10 +36,10 @@ namespace ImageSharp.Drawing.Brushes
///
private static readonly bool[,] Percent20Pattern =
{
- { true, false, true, false },
- { false, false, false, false },
- { false, true, false, true },
- { false, false, false, false }
+ { true, false, false, false },
+ { false, false, true, false },
+ { true, false, false, false },
+ { false, false, true, false }
};
///
@@ -48,7 +47,10 @@ namespace ImageSharp.Drawing.Brushes
///
private static readonly bool[,] HorizontalPattern =
{
- { false, true, false, false },
+ { false },
+ { true },
+ { false },
+ { false }
};
///
@@ -56,7 +58,10 @@ namespace ImageSharp.Drawing.Brushes
///
private static readonly bool[,] MinPattern =
{
- { false, false, false, true },
+ { false },
+ { false },
+ { false },
+ { true }
};
///
@@ -64,10 +69,7 @@ namespace ImageSharp.Drawing.Brushes
///
private static readonly bool[,] VerticalPattern =
{
- { false },
- { true },
- { false },
- { false }
+ { false, true, false, false },
};
///
@@ -75,10 +77,10 @@ namespace ImageSharp.Drawing.Brushes
///
private static readonly bool[,] ForwardDiagonalPattern =
{
- { true, false, false, false },
- { false, true, false, false },
+ { false, false, false, true },
{ false, false, true, false },
- { false, false, false, true }
+ { false, true, false, false },
+ { true, false, false, false }
};
///
@@ -86,10 +88,10 @@ namespace ImageSharp.Drawing.Brushes
///
private static readonly bool[,] BackwardDiagonalPattern =
{
- { false, false, false, true },
- { false, false, true, false },
+ { true, false, false, false },
{ false, true, false, false },
- { true, false, false, false }
+ { false, false, true, false },
+ { false, false, false, true }
};
///
diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs
index 732cd4f8be..080111f610 100644
--- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs
+++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs
@@ -34,7 +34,7 @@ namespace ImageSharp.Drawing.Brushes
///
public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
{
- return new ImageBrushApplicator(this.image, region);
+ return new ImageBrushApplicator(sourcePixels, this.image, region);
}
///
@@ -71,7 +71,11 @@ namespace ImageSharp.Drawing.Brushes
///
/// The region.
///
- public ImageBrushApplicator(IImageBase image, RectangleF region)
+ ///
+ /// The sourcePixels.
+ ///
+ public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region)
+ : base(sourcePixels)
{
this.source = image.Lock();
this.xLength = image.Width;
@@ -87,7 +91,7 @@ namespace ImageSharp.Drawing.Brushes
///
/// The color
///
- public override TColor this[int x, int y]
+ internal override TColor this[int x, int y]
{
get
{
@@ -95,10 +99,10 @@ namespace ImageSharp.Drawing.Brushes
// Offset the requested pixel by the value in the rectangle (the shapes position)
point = point - this.offset;
- x = (int)point.X % this.xLength;
- y = (int)point.Y % this.yLength;
+ int srcX = (int)point.X % this.xLength;
+ int srcY = (int)point.Y % this.yLength;
- return this.source[x, y];
+ return this.source[srcX, srcY];
}
}
@@ -107,6 +111,37 @@ namespace ImageSharp.Drawing.Brushes
{
this.source.Dispose();
}
+
+ ///
+ internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
+
+ using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer))
+ {
+ BufferPointer slice = buffer.Slice(offset);
+
+ for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ {
+ int targetX = xPos + x;
+ int targetY = y;
+
+ float opacity = slice[xPos];
+ if (opacity > Constants.Epsilon)
+ {
+ Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
+
+ Vector4 sourceVector = this[targetX, targetY].ToVector4();
+
+ Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(finalColor);
+ this.Target[targetX, targetY] = packed;
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs
index 741ab3f005..2b4d3ec738 100644
--- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs
+++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs
@@ -30,15 +30,6 @@ namespace ImageSharp.Drawing.Brushes
/// 0
/// 0
///
- /// Warning when use array initializer across multiple lines the bools look inverted i.e.
- /// new bool[,]{
- /// {true, false, false},
- /// {false,true, false}
- /// }
- /// would be
- /// 10
- /// 01
- /// 00
///
/// The pixel format.
public class PatternBrush : IBrush
@@ -47,12 +38,19 @@ namespace ImageSharp.Drawing.Brushes
///
/// The pattern.
///
- private readonly TColor[][] pattern;
+ private readonly Fast2DArray pattern;
+ private readonly Fast2DArray patternVector;
///
- /// The stride width.
+ /// Initializes a new instance of the class.
///
- private readonly int stride;
+ /// Color of the fore.
+ /// Color of the back.
+ /// The pattern.
+ public PatternBrush(TColor foreColor, TColor backColor, bool[,] pattern)
+ : this(foreColor, backColor, new Fast2DArray(pattern))
+ {
+ }
///
/// Initializes a new instance of the class.
@@ -60,26 +58,23 @@ namespace ImageSharp.Drawing.Brushes
/// Color of the fore.
/// Color of the back.
/// The pattern.
- public PatternBrush(TColor foreColor, TColor backColor, bool[,] pattern)
+ internal PatternBrush(TColor foreColor, TColor backColor, Fast2DArray pattern)
{
- this.stride = pattern.GetLength(1);
-
- // Convert the multidimension array into a jagged one.
- int height = pattern.GetLength(0);
- this.pattern = new TColor[height][];
- for (int x = 0; x < height; x++)
+ Vector4 foreColorVector = foreColor.ToVector4();
+ Vector4 backColorVector = backColor.ToVector4();
+ this.pattern = new Fast2DArray(pattern.Width, pattern.Height);
+ this.patternVector = new Fast2DArray(pattern.Width, pattern.Height);
+ for (int i = 0; i < pattern.Data.Length; i++)
{
- this.pattern[x] = new TColor[this.stride];
- for (int y = 0; y < this.stride; y++)
+ if (pattern.Data[i])
{
- if (pattern[x, y])
- {
- this.pattern[x][y] = foreColor;
- }
- else
- {
- this.pattern[x][y] = backColor;
- }
+ this.pattern.Data[i] = foreColor;
+ this.patternVector.Data[i] = foreColorVector;
+ }
+ else
+ {
+ this.pattern.Data[i] = backColor;
+ this.patternVector.Data[i] = backColorVector;
}
}
}
@@ -91,13 +86,13 @@ namespace ImageSharp.Drawing.Brushes
internal PatternBrush(PatternBrush brush)
{
this.pattern = brush.pattern;
- this.stride = brush.stride;
+ this.patternVector = brush.patternVector;
}
///
public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
{
- return new PatternBrushApplicator(this.pattern, this.stride);
+ return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector);
}
///
@@ -105,31 +100,23 @@ namespace ImageSharp.Drawing.Brushes
///
private class PatternBrushApplicator : BrushApplicator
{
- ///
- /// The patter x-length.
- ///
- private readonly int xLength;
-
- ///
- /// The stride width.
- ///
- private readonly int stride;
-
///
/// The pattern.
///
- private readonly TColor[][] pattern;
+ private readonly Fast2DArray pattern;
+ private readonly Fast2DArray patternVector;
///
/// Initializes a new instance of the class.
///
+ /// The sourcePixels.
/// The pattern.
- /// The stride.
- public PatternBrushApplicator(TColor[][] pattern, int stride)
+ /// The patternVector.
+ public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector)
+ : base(sourcePixels)
{
this.pattern = pattern;
- this.xLength = pattern.Length;
- this.stride = stride;
+ this.patternVector = patternVector;
}
///
@@ -140,14 +127,15 @@ namespace ImageSharp.Drawing.Brushes
///
/// The Color.
///
- public override TColor this[int x, int y]
+ internal override TColor this[int x, int y]
{
get
{
- x = x % this.xLength;
- y = y % this.stride;
+ x = x % this.pattern.Width;
+ y = y % this.pattern.Height;
- return this.pattern[x][y];
+ // 2d array index at row/column
+ return this.pattern[y, x];
}
}
@@ -156,6 +144,38 @@ namespace ImageSharp.Drawing.Brushes
{
// noop
}
+
+ ///
+ internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
+
+ using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer))
+ {
+ BufferPointer slice = buffer.Slice(offset);
+
+ for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ {
+ int targetX = xPos + x;
+ int targetY = y;
+
+ float opacity = slice[xPos];
+ if (opacity > Constants.Epsilon)
+ {
+ Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
+
+ // 2d array index at row/column
+ Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width];
+
+ Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(finalColor);
+ this.Target[targetX, targetY] = packed;
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
index b66827e491..2c460e88e0 100644
--- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
+++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
@@ -7,6 +7,7 @@ namespace ImageSharp.Drawing.Processors
{
using System;
using System.Numerics;
+ using System.Runtime.CompilerServices;
///
/// primitive that converts a point in to a color for discovering the fill color based on an implementation
@@ -16,15 +17,68 @@ namespace ImageSharp.Drawing.Processors
public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush
where TColor : struct, IPixel
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The target.
+ internal BrushApplicator(PixelAccessor target)
+ {
+ this.Target = target;
+ }
+
+ ///
+ /// Gets the destinaion
+ ///
+ protected PixelAccessor Target { get; }
+
///
/// Gets the color for a single pixel.
///
/// The x cordinate.
/// The y cordinate.
/// The a that should be applied to the pixel.
- public abstract TColor this[int x, int y] { get; }
+ internal abstract TColor this[int x, int y] { get; }
///
public abstract void Dispose();
+
+ ///
+ /// Applies the opactiy weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
+ ///
+ /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.
+ /// The number of pixels effected by this scanline.
+ /// The offset fromthe begining of the opacity data starts.
+ /// The x position in the target pixel space that the start of the scanline data corresponds to.
+ /// The y position in the target pixel space that whole scanline corresponds to.
+ /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.
+ internal virtual void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ {
+ DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
+
+ using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer))
+ {
+ BufferPointer slice = buffer.Slice(offset);
+
+ for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ {
+ int targetX = xPos + x;
+ int targetY = y;
+
+ float opacity = slice[xPos];
+ if (opacity > Constants.Epsilon)
+ {
+ Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
+
+ Vector4 sourceVector = this[targetX, targetY].ToVector4();
+
+ Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(finalColor);
+ this.Target[targetX, targetY] = packed;
+ }
+ }
+ }
+ }
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs
index db8f3705e3..0c6e86643f 100644
--- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs
+++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs
@@ -65,11 +65,6 @@ namespace ImageSharp.Drawing.Brushes
///
private class RecolorBrushApplicator : BrushApplicator
{
- ///
- /// The source pixel accessor.
- ///
- private readonly PixelAccessor source;
-
///
/// The source color.
///
@@ -93,8 +88,8 @@ namespace ImageSharp.Drawing.Brushes
/// Color of the target.
/// The threshold .
public RecolorBrushApplicator(PixelAccessor sourcePixels, TColor sourceColor, TColor targetColor, float threshold)
+ : base(sourcePixels)
{
- this.source = sourcePixels;
this.sourceColor = sourceColor.ToVector4();
this.targetColor = targetColor.ToVector4();
@@ -114,12 +109,12 @@ namespace ImageSharp.Drawing.Brushes
///
/// The color
///
- public override TColor this[int x, int y]
+ internal override TColor this[int x, int y]
{
get
{
// Offset the requested pixel by the value in the rectangle (the shapes position)
- TColor result = this.source[x, y];
+ TColor result = this.Target[x, y];
Vector4 background = result.ToVector4();
float distance = Vector4.DistanceSquared(background, this.sourceColor);
if (distance <= this.threshold)
@@ -140,6 +135,46 @@ namespace ImageSharp.Drawing.Brushes
public override void Dispose()
{
}
+
+ ///
+ internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
+
+ using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer))
+ {
+ BufferPointer slice = buffer.Slice(offset);
+
+ for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ {
+ int targetX = xPos + x;
+ int targetY = y;
+
+ float opacity = slice[xPos];
+ if (opacity > Constants.Epsilon)
+ {
+ Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
+
+ Vector4 sourceVector = backgroundVector;
+ float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor);
+ if (distance <= this.threshold)
+ {
+ float lerpAmount = (this.threshold - distance) / this.threshold;
+ sourceVector = Vector4BlendTransforms.PremultipliedLerp(
+ sourceVector,
+ this.targetColor,
+ lerpAmount);
+
+ Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(finalColor);
+ this.Target[targetX, targetY] = packed;
+ }
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs
index 30351dbe1b..e001829b01 100644
--- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs
+++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs
@@ -42,7 +42,7 @@ namespace ImageSharp.Drawing.Brushes
///
public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region)
{
- return new SolidBrushApplicator(this.color);
+ return new SolidBrushApplicator(sourcePixels, this.color);
}
///
@@ -54,14 +54,18 @@ namespace ImageSharp.Drawing.Brushes
/// The solid color.
///
private readonly TColor color;
+ private readonly Vector4 colorVector;
///
/// Initializes a new instance of the class.
///
/// The color.
- public SolidBrushApplicator(TColor color)
+ /// The sourcePixels.
+ public SolidBrushApplicator(PixelAccessor sourcePixels, TColor color)
+ : base(sourcePixels)
{
this.color = color;
+ this.colorVector = color.ToVector4();
}
///
@@ -72,13 +76,43 @@ namespace ImageSharp.Drawing.Brushes
///
/// The color
///
- public override TColor this[int x, int y] => this.color;
+ internal override TColor this[int x, int y] => this.color;
///
public override void Dispose()
{
// noop
}
+
+ ///
+ internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
+
+ using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer))
+ {
+ BufferPointer slice = buffer.Slice(offset);
+
+ for (int xPos = 0; xPos < scanlineWidth; xPos++)
+ {
+ int targetX = xPos + x;
+ int targetY = y;
+
+ float opacity = slice[xPos];
+ if (opacity > Constants.Epsilon)
+ {
+ Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
+ Vector4 sourceVector = this.colorVector;
+
+ Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
+
+ TColor packed = default(TColor);
+ packed.PackFromVector4(finalColor);
+ this.Target[targetX, targetY] = packed;
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/GraphicsOptions.cs b/src/ImageSharp.Drawing/GraphicsOptions.cs
index 7e569c9a5f..a21617eadf 100644
--- a/src/ImageSharp.Drawing/GraphicsOptions.cs
+++ b/src/ImageSharp.Drawing/GraphicsOptions.cs
@@ -20,6 +20,11 @@ namespace ImageSharp.Drawing
///
public bool Antialias;
+ ///
+ /// The number of subpixels to use while rendering with antialiasing enabled.
+ ///
+ public int AntialiasSubpixelDepth;
+
///
/// Initializes a new instance of the struct.
///
@@ -27,6 +32,7 @@ namespace ImageSharp.Drawing
public GraphicsOptions(bool enableAntialiasing)
{
this.Antialias = enableAntialiasing;
+ this.AntialiasSubpixelDepth = 16;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index f690dac8d5..e15bfe74ba 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -40,7 +40,7 @@
All
-
+
..\..\ImageSharp.ruleset
diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs
index b02c5c2e5b..0868945318 100644
--- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs
+++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs
@@ -5,6 +5,7 @@
namespace ImageSharp.Drawing
{
+ using System;
using System.Buffers;
using System.Numerics;
@@ -39,30 +40,7 @@ namespace ImageSharp.Drawing
public override Rectangle Bounds { get; }
///
- public override int ScanX(int x, float[] buffer, int length, int offset)
- {
- Vector2 start = new Vector2(x, this.Bounds.Top - 1);
- Vector2 end = new Vector2(x, this.Bounds.Bottom + 1);
- Vector2[] innerbuffer = ArrayPool.Shared.Rent(length);
- try
- {
- int count = this.Shape.FindIntersections(start, end, innerbuffer, length, 0);
-
- for (int i = 0; i < count; i++)
- {
- buffer[i + offset] = innerbuffer[i].Y;
- }
-
- return count;
- }
- finally
- {
- ArrayPool.Shared.Return(innerbuffer);
- }
- }
-
- ///
- public override int ScanY(int y, float[] buffer, int length, int offset)
+ public override int Scan(float y, float[] buffer, int length, int offset)
{
Vector2 start = new Vector2(this.Bounds.Left - 1, y);
Vector2 end = new Vector2(this.Bounds.Right + 1, y);
diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
index 67b4aab5f7..66e1f4380c 100644
--- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
@@ -57,315 +57,116 @@ namespace ImageSharp.Drawing.Processors
///
protected override void OnApply(ImageBase source, Rectangle sourceRectangle)
{
- Rectangle rect = this.Region.Bounds;
-
- int polyStartY = sourceRectangle.Y - DrawPadding;
- int polyEndY = sourceRectangle.Bottom + DrawPadding;
- int startX = sourceRectangle.X - DrawPadding;
- int endX = sourceRectangle.Right + DrawPadding;
-
- int minX = Math.Max(sourceRectangle.Left, startX);
- int maxX = Math.Min(sourceRectangle.Right - 1, endX);
- int minY = Math.Max(sourceRectangle.Top, polyStartY);
- int maxY = Math.Min(sourceRectangle.Bottom - 1, polyEndY);
+ Region region = this.Region;
+ Rectangle rect = region.Bounds;
// Align start/end positions.
- minX = Math.Max(0, minX);
- maxX = Math.Min(source.Width, maxX);
- minY = Math.Max(0, minY);
- maxY = Math.Min(source.Height, maxY);
+ 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);
ArrayPool arrayPool = ArrayPool.Shared;
- int maxIntersections = this.Region.MaxIntersections;
+ int maxIntersections = region.MaxIntersections;
+ float subpixelCount = 4;
+ if (this.Options.Antialias)
+ {
+ subpixelCount = this.Options.AntialiasSubpixelDepth;
+ if (subpixelCount < 4)
+ {
+ subpixelCount = 4;
+ }
+ }
using (PixelAccessor sourcePixels = source.Lock())
using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect))
{
- Parallel.For(
- minY,
- maxY,
- this.ParallelOptions,
- (int y) =>
+ float[] buffer = arrayPool.Rent(maxIntersections);
+ int scanlineWidth = maxX - minX;
+ float[] scanline = ArrayPool.Shared.Rent(scanlineWidth);
+ try
{
- float[] buffer = arrayPool.Rent(maxIntersections);
-
- try
+ bool scanlineDirty = true;
+ for (int y = minY; y < maxY; y++)
{
- float right = endX;
-
- // foreach line we get all the points where this line crosses the polygon
- int pointsFound = this.Region.ScanY(y, buffer, maxIntersections, 0);
- if (pointsFound == 0)
- {
- // nothing on this line skip
- return;
- }
-
- QuickSort(buffer, pointsFound);
-
- int currentIntersection = 0;
- float nextPoint = buffer[0];
- float lastPoint = float.MinValue;
- bool isInside = false;
-
- for (int x = minX; x < maxX; x++)
+ if (scanlineDirty)
{
- if (!isInside)
- {
- if (x < (nextPoint - DrawPadding) && x > (lastPoint + DrawPadding))
- {
- if (nextPoint == right)
- {
- // we are in the ends run skip it
- x = maxX;
- continue;
- }
-
- // lets just jump forward
- x = (int)Math.Floor(nextPoint) - DrawPadding;
- }
- }
-
- bool onCorner = false;
-
- // there seems to be some issue with this switch.
- if (x >= nextPoint)
+ // clear the buffer
+ for (int x = 0; x < scanlineWidth; x++)
{
- currentIntersection++;
- lastPoint = nextPoint;
- if (currentIntersection == pointsFound)
- {
- nextPoint = right;
- }
- else
- {
- nextPoint = buffer[currentIntersection];
-
- // double point from a corner flip the bit back and move on again
- if (nextPoint == lastPoint)
- {
- onCorner = true;
- isInside ^= true;
- currentIntersection++;
- if (currentIntersection == pointsFound)
- {
- nextPoint = right;
- }
- else
- {
- nextPoint = buffer[currentIntersection];
- }
- }
- }
-
- isInside ^= true;
- }
-
- float opacity = 1;
- if (!isInside && !onCorner)
- {
- if (this.Options.Antialias)
- {
- float distance = float.MaxValue;
- if (x == lastPoint || x == nextPoint)
- {
- // we are to far away from the line
- distance = 0;
- }
- else if (nextPoint - AntialiasFactor < x)
- {
- // we are near the left of the line
- distance = nextPoint - x;
- }
- else if (lastPoint + AntialiasFactor > x)
- {
- // we are near the right of the line
- distance = x - lastPoint;
- }
- else
- {
- // we are to far away from the line
- continue;
- }
- opacity = 1 - (distance / AntialiasFactor);
- }
- else
- {
- continue;
- }
+ scanline[x] = 0;
}
- if (opacity > Constants.Epsilon)
- {
- Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
- Vector4 sourceVector = applicator[x, y].ToVector4();
-
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
-
- TColor packed = default(TColor);
- packed.PackFromVector4(finalColor);
- sourcePixels[x, y] = packed;
- }
+ scanlineDirty = false;
}
- }
- finally
- {
- arrayPool.Return(buffer);
- }
- });
-
- if (this.Options.Antialias)
- {
- // we only need to do the X can for antialiasing purposes
- Parallel.For(
- minX,
- maxX,
- this.ParallelOptions,
- (int x) =>
- {
- float[] buffer = arrayPool.Rent(maxIntersections);
- try
+ float subpixelFraction = 1f / subpixelCount;
+ float subpixelFractionPoint = subpixelFraction / subpixelCount;
+ for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
- float left = polyStartY;
- float right = polyEndY;
-
- // foreach line we get all the points where this line crosses the polygon
- int pointsFound = this.Region.ScanX(x, buffer, maxIntersections, 0);
+ int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
if (pointsFound == 0)
{
- // nothign on this line skip
- return;
+ // nothing on this line skip
+ continue;
}
QuickSort(buffer, pointsFound);
- int currentIntersection = 0;
- float nextPoint = buffer[0];
- float lastPoint = left;
- bool isInside = false;
-
- for (int y = minY; y < maxY; y++)
+ for (int point = 0; point < pointsFound; point += 2)
{
- if (!isInside)
- {
- if (y < (nextPoint - DrawPadding) && y > (lastPoint + DrawPadding))
- {
- if (nextPoint == right)
- {
- // we are in the ends run skip it
- y = maxY;
- continue;
- }
+ // points will be paired up
+ float scanStart = buffer[point] - minX;
+ float scanEnd = buffer[point + 1] - minX;
+ int startX = (int)Math.Floor(scanStart);
+ int endX = (int)Math.Floor(scanEnd);
- // lets just jump forward
- y = (int)Math.Floor(nextPoint) - DrawPadding;
- }
- }
- else
+ for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
- if (y < nextPoint - DrawPadding)
- {
- if (nextPoint == right)
- {
- // we are in the ends run skip it
- y = maxY;
- continue;
- }
-
- // lets just jump forward
- y = (int)Math.Floor(nextPoint);
- }
+ scanline[startX] += subpixelFractionPoint;
+ scanlineDirty = true;
}
- bool onCorner = false;
-
- if (y >= nextPoint)
+ for (float x = endX; x < scanEnd; x += subpixelFraction)
{
- currentIntersection++;
- lastPoint = nextPoint;
- if (currentIntersection == pointsFound)
- {
- nextPoint = right;
- }
- else
- {
- nextPoint = buffer[currentIntersection];
-
- // double point from a corner flip the bit back and move on again
- if (nextPoint == lastPoint)
- {
- onCorner = true;
- isInside ^= true;
- currentIntersection++;
- if (currentIntersection == pointsFound)
- {
- nextPoint = right;
- }
- else
- {
- nextPoint = buffer[currentIntersection];
- }
- }
- }
+ scanline[endX] += subpixelFractionPoint;
+ scanlineDirty = true;
+ }
- isInside ^= true;
+ for (int x = startX + 1; x < endX; x++)
+ {
+ scanline[x] += subpixelFraction;
+ scanlineDirty = true;
}
+ }
+ }
- float opacity = 1;
- if (!isInside && !onCorner)
+ if (scanlineDirty)
+ {
+ if (!this.Options.Antialias)
+ {
+ for (int x = 0; x < scanlineWidth; x++)
{
- if (this.Options.Antialias)
+ if (scanline[x] > 0.5)
{
- float distance = float.MaxValue;
- if (y == lastPoint || y == nextPoint)
- {
- // we are to far away from the line
- distance = 0;
- }
- else if (nextPoint - AntialiasFactor < y)
- {
- // we are near the left of the line
- distance = nextPoint - y;
- }
- else if (lastPoint + AntialiasFactor > y)
- {
- // we are near the right of the line
- distance = y - lastPoint;
- }
- else
- {
- // we are to far away from the line
- continue;
- }
- opacity = 1 - (distance / AntialiasFactor);
+ scanline[x] = 1;
}
else
{
- continue;
+ scanline[x] = 0;
}
}
-
- // don't set full opactiy color as it will have been gotten by the first scan
- if (opacity > Constants.Epsilon && opacity < 1)
- {
- Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
- Vector4 sourceVector = applicator[x, y].ToVector4();
- Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
- finalColor.W = backgroundVector.W;
-
- TColor packed = default(TColor);
- packed.PackFromVector4(finalColor);
- sourcePixels[x, y] = packed;
- }
}
+
+ applicator.Apply(scanline, scanlineWidth, 0, minX, y);
}
- finally
- {
- arrayPool.Return(buffer);
- }
- });
+ }
+ }
+ finally
+ {
+ arrayPool.Return(buffer);
+ ArrayPool.Shared.Return(scanline);
}
}
}
diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs
index fe1dc52221..8cab885021 100644
--- a/src/ImageSharp.Drawing/Region.cs
+++ b/src/ImageSharp.Drawing/Region.cs
@@ -19,28 +19,18 @@ namespace ImageSharp.Drawing
/// Gets the bounding box that entirely surrounds this region.
///
///
- /// This should always contains all possible points returned from either or .
+ /// This should always contains all possible points returned from .
///
public abstract Rectangle Bounds { get; }
///
- /// Scans the X axis for intersections.
- ///
- /// The position along the X axis to find intersections.
- /// The buffer.
- /// The length.
- /// The offset.
- /// The number of intersections found.
- public abstract int ScanX(int x, float[] buffer, int length, int offset);
-
- ///
- /// Scans the Y axis for intersections.
+ /// Scans the X axis for intersections at the Y axis position.
///
/// The position along the y axis to find intersections.
/// The buffer.
/// The length.
/// The offset.
/// The number of intersections found.
- public abstract int ScanY(int y, float[] buffer, int length, int offset);
+ public abstract int Scan(float y, float[] buffer, int length, int offset);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs
index e707ef5e50..253bb2aaca 100644
--- a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs
+++ b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs
@@ -20,6 +20,11 @@ namespace ImageSharp.Drawing
///
public bool Antialias;
+ ///
+ /// The number of subpixels to use while rendering with antialiasing enabled.
+ ///
+ public int AntialiasSubpixelDepth;
+
///
/// Whether the text should be drawing with kerning enabled.
///
@@ -39,6 +44,7 @@ namespace ImageSharp.Drawing
this.Antialias = enableAntialiasing;
this.ApplyKerning = true;
this.TabWidth = 4;
+ this.AntialiasSubpixelDepth = 16;
}
///
@@ -50,7 +56,10 @@ namespace ImageSharp.Drawing
///
public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{
- return new TextGraphicsOptions(options.Antialias);
+ return new TextGraphicsOptions(options.Antialias)
+ {
+ AntialiasSubpixelDepth = options.AntialiasSubpixelDepth
+ };
}
///
@@ -62,7 +71,10 @@ namespace ImageSharp.Drawing
///
public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{
- return new GraphicsOptions(options.Antialias);
+ return new GraphicsOptions(options.Antialias)
+ {
+ AntialiasSubpixelDepth = options.AntialiasSubpixelDepth
+ };
}
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs
index 1eafbe077f..782306deb7 100644
--- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs
+++ b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs
@@ -9,6 +9,7 @@ namespace ImageSharp.Benchmarks
using System.Drawing.Drawing2D;
using System.IO;
using System.Numerics;
+ using SixLabors.Shapes;
using BenchmarkDotNet.Attributes;
@@ -17,6 +18,15 @@ namespace ImageSharp.Benchmarks
public class FillPolygon : BenchmarkBase
{
+ private readonly Polygon shape;
+
+ public FillPolygon()
+ {
+ this.shape = new SixLabors.Shapes.Polygon(new LinearLineSegment(new Vector2(10, 10),
+ new Vector2(550, 50),
+ new Vector2(200, 400)));
+ }
+
[Benchmark(Baseline = true, Description = "System.Drawing Fill Polygon")]
public void DrawSolidPolygonSystemDrawing()
{
@@ -60,5 +70,21 @@ namespace ImageSharp.Benchmarks
}
}
}
+
+ [Benchmark(Description = "ImageSharp Fill Polygon - cached shape")]
+ public void DrawSolidPolygonCoreCahced()
+ {
+ using (CoreImage image = new CoreImage(800, 800))
+ {
+ image.Fill(
+ CoreColor.HotPink,
+ this.shape);
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ image.SaveAsBmp(ms);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs
index d3c1877abd..8162bc5319 100644
--- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs
@@ -33,8 +33,9 @@ namespace ImageSharp.Tests.Drawing
{
// lets pick random spots to start checking
Random r = new Random();
- int xStride = expectedPattern.GetLength(1);
- int yStride = expectedPattern.GetLength(0);
+ Fast2DArray expectedPatternFast = new Fast2DArray(expectedPattern);
+ int xStride = expectedPatternFast.Width;
+ int yStride = expectedPatternFast.Height;
int offsetX = r.Next(image.Width / xStride) * xStride;
int offsetY = r.Next(image.Height / yStride) * yStride;
for (int x = 0; x < xStride; x++)
@@ -43,7 +44,7 @@ namespace ImageSharp.Tests.Drawing
{
int actualX = x + offsetX;
int actualY = y + offsetY;
- Color expected = expectedPattern[y, x]; // inverted pattern
+ Color expected = expectedPatternFast[y, x]; // inverted pattern
Color actual = sourcePixels[actualX, actualY];
if (expected != actual)
{
@@ -187,10 +188,10 @@ namespace ImageSharp.Tests.Drawing
{
Test("ForwardDiagonal", Color.Blue, Brushes.ForwardDiagonal(Color.HotPink, Color.LimeGreen),
new Color[,] {
- { Color.HotPink, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen},
- { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen},
+ { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.HotPink},
{ Color.LimeGreen, Color.LimeGreen, Color.HotPink, Color.LimeGreen},
- { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.HotPink}
+ { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen},
+ { Color.HotPink, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}
});
}
@@ -199,10 +200,10 @@ namespace ImageSharp.Tests.Drawing
{
Test("ForwardDiagonal_Transparent", Color.Blue, Brushes.ForwardDiagonal(Color.HotPink),
new Color[,] {
- { Color.HotPink, Color.Blue, Color.Blue, Color.Blue},
- { Color.Blue, Color.HotPink, Color.Blue, Color.Blue},
+ { Color.Blue, Color.Blue, Color.Blue, Color.HotPink},
{ Color.Blue, Color.Blue, Color.HotPink, Color.Blue},
- { Color.Blue, Color.Blue, Color.Blue, Color.HotPink}
+ { Color.Blue, Color.HotPink, Color.Blue, Color.Blue},
+ { Color.HotPink, Color.Blue, Color.Blue, Color.Blue}
});
}
@@ -211,10 +212,10 @@ namespace ImageSharp.Tests.Drawing
{
Test("BackwardDiagonal", Color.Blue, Brushes.BackwardDiagonal(Color.HotPink, Color.LimeGreen),
new Color[,] {
- { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.HotPink},
- { Color.LimeGreen, Color.LimeGreen, Color.HotPink, Color.LimeGreen},
- { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen},
- { Color.HotPink, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}
+ { Color.HotPink, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen},
+ { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen},
+ { Color.LimeGreen, Color.LimeGreen, Color.HotPink, Color.LimeGreen},
+ { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.HotPink}
});
}
@@ -223,13 +224,11 @@ namespace ImageSharp.Tests.Drawing
{
Test("BackwardDiagonal_Transparent", Color.Blue, Brushes.BackwardDiagonal(Color.HotPink),
new Color[,] {
- { Color.Blue, Color.Blue, Color.Blue, Color.HotPink},
- { Color.Blue, Color.Blue, Color.HotPink, Color.Blue},
- { Color.Blue, Color.HotPink, Color.Blue, Color.Blue},
- { Color.HotPink, Color.Blue, Color.Blue, Color.Blue}
+ { Color.HotPink, Color.Blue, Color.Blue, Color.Blue},
+ { Color.Blue, Color.HotPink, Color.Blue, Color.Blue},
+ { Color.Blue, Color.Blue, Color.HotPink, Color.Blue},
+ { Color.Blue, Color.Blue, Color.Blue, Color.HotPink}
});
}
-
-
}
}
diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs
new file mode 100644
index 0000000000..db9686f3d2
--- /dev/null
+++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs
@@ -0,0 +1,46 @@
+
+namespace ImageSharp.Tests.Drawing
+{
+ using System;
+ using System.IO;
+ using ImageSharp;
+ using ImageSharp.Drawing.Brushes;
+ using Processing;
+ using System.Collections.Generic;
+ using Xunit;
+ using ImageSharp.Drawing;
+ using System.Numerics;
+ using SixLabors.Shapes;
+ using ImageSharp.Drawing.Processors;
+ using ImageSharp.Drawing.Pens;
+ using Moq;
+ using System.Collections.Immutable;
+
+ public class FillRegionProcessorTests
+ {
+ [Theory]
+ [InlineData(true, 1, 4)]
+ [InlineData(true, 2, 4)]
+ [InlineData(true, 5, 5)]
+ [InlineData(true, 8, 8)]
+ [InlineData(false, 8, 4)]
+ [InlineData(false, 16, 4)] // we always do 4 sub=pixels when antialising is off.
+ public void MinimumAntialiasSubpixelDepth(bool antialias, int antialiasSubpixelDepth, int expectedAntialiasSubpixelDepth)
+ {
+ var bounds = new ImageSharp.Rectangle(0, 0, 1, 1);
+
+ Mock> brush = new Mock>();
+ Mock region = new Mock();
+ region.Setup(x => x.Bounds).Returns(bounds);
+
+ GraphicsOptions options = new GraphicsOptions(antialias) {
+ AntialiasSubpixelDepth = 1
+ };
+ FillRegionProcessor processor = new FillRegionProcessor(brush.Object, region.Object, options);
+ Image img = new Image(1, 1);
+ processor.Apply(img, bounds);
+
+ region.Verify(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(4));
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs
index aa7c0575c7..78c3492332 100644
--- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs
@@ -67,25 +67,6 @@ namespace ImageSharp.Tests.Drawing.Paths
pathMock.Verify(x => x.MaxIntersections);
}
- [Fact]
- public void ShapeRegionFromPathScanXProxyToShape()
- {
- int xToScan = 10;
- ShapeRegion region = new ShapeRegion(pathMock.Object);
-
- pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
- .Callback((s, e, b, c, o) => {
- Assert.Equal(xToScan, s.X);
- Assert.Equal(xToScan, e.X);
- Assert.True(s.Y < bounds.Top);
- Assert.True(e.Y > bounds.Bottom);
- }).Returns(0);
-
- int i = region.ScanX(xToScan, new float[0], 0, 0);
-
- pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
- }
-
[Fact]
public void ShapeRegionFromPathScanYProxyToShape()
{
@@ -100,27 +81,7 @@ namespace ImageSharp.Tests.Drawing.Paths
Assert.True(e.X > bounds.Right);
}).Returns(0);
- int i = region.ScanY(yToScan, new float[0], 0, 0);
-
- pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
- }
-
-
- [Fact]
- public void ShapeRegionFromShapeScanXProxyToShape()
- {
- int xToScan = 10;
- ShapeRegion region = new ShapeRegion(pathMock.Object);
-
- pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
- .Callback((s, e, b, c, o) => {
- Assert.Equal(xToScan, s.X);
- Assert.Equal(xToScan, e.X);
- Assert.True(s.Y < bounds.Top);
- Assert.True(e.Y > bounds.Bottom);
- }).Returns(0);
-
- int i = region.ScanX(xToScan, new float[0], 0, 0);
+ int i = region.Scan(yToScan, new float[0], 0, 0);
pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
}
@@ -139,7 +100,7 @@ namespace ImageSharp.Tests.Drawing.Paths
Assert.True(e.X > bounds.Right);
}).Returns(0);
- int i = region.ScanY(yToScan, new float[0], 0, 0);
+ int i = region.Scan(yToScan, new float[0], 0, 0);
pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
}
diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs
index 80713468d4..1a7e98a12f 100644
--- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs
@@ -36,15 +36,9 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- //top of curve
- Assert.Equal(Color.HotPink, sourcePixels[138, 116]);
-
- //start points
- Assert.Equal(Color.HotPink, sourcePixels[10, 400]);
- Assert.Equal(Color.HotPink, sourcePixels[300, 400]);
+ Assert.Equal(Color.HotPink, sourcePixels[150, 300]);
//curve points should not be never be set
- Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should not be empty
@@ -83,12 +77,7 @@ namespace ImageSharp.Tests.Drawing
//top of curve
Assert.Equal(mergedColor, sourcePixels[138, 116]);
- //start points
- Assert.Equal(mergedColor, sourcePixels[10, 400]);
- Assert.Equal(mergedColor, sourcePixels[300, 400]);
-
//curve points should not be never be set
- Assert.Equal(Color.Blue, sourcePixels[30, 10]);
Assert.Equal(Color.Blue, sourcePixels[240, 30]);
// inside shape should not be empty
diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs
index ce13d9f0f6..4ff250a934 100644
--- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs
@@ -42,20 +42,10 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
-
- Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
-
- Assert.Equal(Color.HotPink, sourcePixels[70, 137]);
-
- Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
-
- Assert.Equal(Color.HotPink, sourcePixels[35, 100]);
-
- Assert.Equal(Color.Blue, sourcePixels[2, 2]);
+ Assert.Equal(Color.HotPink, sourcePixels[20, 35]);
//inside hole
- Assert.Equal(Color.Blue, sourcePixels[57, 99]);
+ Assert.Equal(Color.Blue, sourcePixels[60, 100]);
}
}
}
@@ -87,18 +77,10 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
-
- Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
-
- Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
-
- Assert.Equal(Color.HotPink, sourcePixels[35, 100]);
-
- Assert.Equal(Color.Blue, sourcePixels[2, 2]);
+ Assert.Equal(Color.HotPink, sourcePixels[20, 35]);
//inside hole
- Assert.Equal(Color.Blue, sourcePixels[57, 99]);
+ Assert.Equal(Color.Blue, sourcePixels[60, 100]);
}
}
}
@@ -133,18 +115,10 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- Assert.Equal(mergedColor, sourcePixels[11, 11]);
-
- Assert.Equal(mergedColor, sourcePixels[200, 150]);
-
- Assert.Equal(mergedColor, sourcePixels[50, 50]);
-
- Assert.Equal(mergedColor, sourcePixels[35, 100]);
-
- Assert.Equal(Color.Blue, sourcePixels[2, 2]);
+ Assert.Equal(mergedColor, sourcePixels[20, 35]);
//inside hole
- Assert.Equal(Color.Blue, sourcePixels[57, 99]);
+ Assert.Equal(Color.Blue, sourcePixels[60, 100]);
}
}
}
diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
index 54c4af38b1..79363480fc 100644
--- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
@@ -38,13 +38,33 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- Assert.Equal(Color.HotPink, sourcePixels[11, 11]);
+ Assert.Equal(Color.HotPink, sourcePixels[81, 145]);
+ }
+ }
+ }
- Assert.Equal(Color.HotPink, sourcePixels[200, 150]);
+ [Fact]
+ public void ImageShouldBeOverlayedByFilledPolygonWithPattern()
+ {
+ string path = this.CreateOutputDirectory("Drawing", "FilledPolygons");
+ Vector2[] simplePath = new[] {
+ new Vector2(10, 10),
+ new Vector2(200, 150),
+ new Vector2(50, 300)
+ };
- Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
+ using (Image image = new Image(500, 500))
+ {
+ using (FileStream output = File.OpenWrite($"{path}/Pattern.png"))
+ {
+ image
+ .FillPolygon(Brushes.Horizontal(Color.HotPink), simplePath, new GraphicsOptions(true))
+ .Save(output);
+ }
- Assert.NotEqual(Color.HotPink, sourcePixels[2, 2]);
+ using (PixelAccessor sourcePixels = image.Lock())
+ {
+ Assert.Equal(Color.HotPink, sourcePixels[81, 145]);
}
}
}
@@ -129,12 +149,6 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- Assert.Equal(mergedColor, sourcePixels[11, 11]);
-
- Assert.Equal(mergedColor, sourcePixels[200, 150]);
-
- Assert.Equal(mergedColor, sourcePixels[50, 50]);
-
Assert.Equal(Color.Blue, sourcePixels[2, 2]);
}
}
@@ -187,19 +201,9 @@ namespace ImageSharp.Tests.Drawing
using (PixelAccessor sourcePixels = image.Lock())
{
- Assert.Equal(Color.HotPink, sourcePixels[25, 35]);
-
- Assert.Equal(Color.HotPink, sourcePixels[50, 79]);
-
- Assert.Equal(Color.HotPink, sourcePixels[75, 35]);
+ Assert.Equal(Color.Blue, sourcePixels[30, 65]);
Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
-
- Assert.Equal(Color.Blue, sourcePixels[2, 2]);
-
- Assert.Equal(Color.Blue, sourcePixels[28, 60]);
-
- Assert.Equal(Color.Blue, sourcePixels[67, 67]);
}
}
}
diff --git a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs
new file mode 100644
index 0000000000..a291a7d18b
--- /dev/null
+++ b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs
@@ -0,0 +1,43 @@
+
+namespace ImageSharp.Tests.Drawing.Text
+{
+ using ImageSharp.Drawing;
+ using SixLabors.Fonts;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Numerics;
+ using System.Threading.Tasks;
+ using Xunit;
+
+ public class TextGraphicsOptionsTests
+ {
+ [Fact]
+ public void ExplicitCastOfGraphicsOptions()
+ {
+ GraphicsOptions opt = new GraphicsOptions(false)
+ {
+ AntialiasSubpixelDepth = 99
+ };
+
+ TextGraphicsOptions textOptions = opt;
+
+ Assert.Equal(false, textOptions.Antialias);
+ Assert.Equal(99, textOptions.AntialiasSubpixelDepth);
+ }
+
+ [Fact]
+ public void ImplicitCastToGraphicsOptions()
+ {
+ TextGraphicsOptions textOptions = new TextGraphicsOptions(false)
+ {
+ AntialiasSubpixelDepth = 99
+ };
+
+ GraphicsOptions opt = (GraphicsOptions)textOptions;
+
+ Assert.Equal(false, opt.Antialias);
+ Assert.Equal(99, opt.AntialiasSubpixelDepth);
+ }
+ }
+}
\ No newline at end of file